DRT modified file to work on any computer

2022-01-17 Some files appeared to be missing to run this notebook. Trying to get all to run

Overview

This is a pipeline for differential analysis of RNASeq data from SKMEL5 sublines using DESeq2 statistical package. Three sublines: SC01 (regressing), SC07 (stationary) and SC10 (expanding) were analyzed for gene expression differences. In addition, time course changes in 8uM PLX4720 were also performed for each subline. Time points are: 0, 3d, 8d. The differential analysis will be performed based on the contrasts defined below. General steps for the analysis are:

###1. Read counts table: + Could be read directly as a csv/txt file. + Alignment and read counts could be done within R environment to create read counts table. 1. Define working directory, load the required libraries. 2. Get read counts table. Read the raw counts file processed by featureCounts. The fastq files were aligned with HiSat2, and the read counts were obtained using featureCounts of Rsubread packages.

pkgs <- c("DESeq2","org.Hs.eg.db","clusterProfiler","HDO.db",
          "pheatmap","ggnewscale","PoiClaClu","enrichR","gtable","Rmisc")
source("getReqdPkgs.r")
getReqdPkgs(pkgs)
suppressPackageStartupMessages(expr={
    library(plyr)
    library(dplyr)
    library(ggplot2)
    library(ggnewscale)
    library(reshape2)
    library(DESeq2)
    library(ggrepel)
    library(pheatmap)
    library(org.Hs.eg.db)
    library(clusterProfiler)
    library("RColorBrewer")
    library(enrichR)
})

SAVEFILES <- FALSE
library(biomaRt)
d <- read.csv("../data/featureCounts_matrix_all.csv", header=T, sep=",")

#Rename columns
cols <- c("ensembl_gene_id", "SC01_day0_rep1", "SC01_day0_rep2", "SC01_day0_rep3",
          "SC01_day3_rep1", "SC01_day3_rep2", "SC01_day3_rep3",
          "SC01_day8_rep1", "SC01_day8_rep2", "SC01_day8_rep3",
          "SC07_day0_rep1", "SC07_day0_rep2", "SC07_day0_rep3",
          "SC07_day3_rep1", "SC07_day3_rep2", "SC07_day3_rep3",
          "SC07_day8_rep1", "SC07_day8_rep2", "SC07_day8_rep3",
          "SC10_day0_rep1", "SC10_day0_rep2", "SC10_day0_rep3",
          "SC10_day3_rep1", "SC10_day3_rep2", "SC10_day3_rep3",
          "SC10_day8_rep1", "SC10_day8_rep2", "SC10_day8_rep3")
names(d) <- cols
ensembl <- useMart("ensembl")
mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
genes <- d$ensembl_gene_id
G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
                filters= "ensembl_gene_id",
                values=genes,
                mart=mart)

GE_data <- merge(d, G_list, by = "ensembl_gene_id")
d <- GE_data[, -1]
d <- d[c(28, seq(1:27))]
rownames(d) <- make.names(d$hgnc_symbol, unique = T)
d <- d[, 2:28]

# remove genes with <5 counts in all samples
d <- d[apply(d, 1, function(x) all(x > 5)),]


countdata <- d

head(countdata)
nrow(countdata)
[1] 14947
ncol(countdata)
[1] 27

Identifying different ion channel gene lists

###2. Convert counts table to DESeq2 object. Convert counts table to object for DESeq2 or any other analysis pipeline. This step will require to prepare data object in a form that is suitable for analysis in DESeq2 pipeline: we will need the following to proceed:

  • countdata: a table with the read/fragment counts.
  • coldata: a table with information about the samples.

Using the matrix of counts and the sample information table, we need to construct the DESeqDataSet object, for which we will use DESeqDataSetFromMatrix…..

1. Define the samples and treatment conditions.

condition <- c("0", "3", "8")
treatment <- rep(condition, each=3) # Three biological replicates
unique(treatment)
[1] "0" "3" "8"
cell <- c("SC01", "SC07","SC10") #sublines used for the analysis
cellName <- rep(cell, each=3)

coldata <- data.frame(cell=rep(cellName), treatment=rep(treatment, each=3))
group = factor(paste(coldata$cell, coldata$treatment, sep="."))
coldata$group = group

2. construct the DESeqDataSet object from the matrix of counts and the sample information table.

Described above are: countdata- raw counts, coldata: sample information table.

dds <- DESeqDataSetFromMatrix(countData = countdata,
                              colData = coldata,
                              design = ~ cell + treatment + cell:treatment)
Warning: some variables in design formula are characters, converting to factors
dds
class: DESeqDataSet 
dim: 14947 27 
metadata(1): version
assays(1): counts
rownames(14947): TSPAN6 DPM1 ... GTF2IP12 X.16619
rowData names(0):
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(3): cell treatment group
nrow(dds); ncol(dds)
[1] 14947
[1] 27

###3. Exploratory analysis and visualization. There are two separate steps in the workflow; the one which involves data transformations in order to visualize sample relationships and the second step involves statistical testing methods which requires the original raw counts.

1. Pre-filtering and normalization.

Pre-filtering and normalization is required to remove lowly expressed genes.

dds2 <- dds[rowSums(counts(dds)) > 18, ] # remove rows with minimum of 2 read per condition

nrow(dds2)
[1] 14947
# save(dds2, file = "DDS_SC-1,7,10_cell-treat-int.RData")
# load("DDS_SC-1,7,10_cell-treat-int.RData")

2. Visualize sample-to-sample distances.

We could use Principal Component Analysis (PCA) to visualize relationships between samples.

rld <- rlog(dds2, blind = FALSE)
# save(rld, file = "RLD_SC-1,7,10_0,3,8d_20180701.RData")
# load("RLD_SC-1,7,10_0,3,8d_20180701.RData")
plotPCA(rld, intgroup = c("cell", "treatment"), ntop=5000)


## Use prcomp function
# Colored by cell line, shape by time point, lines connecting time
pca_DEseq <- prcomp(t(assay(rld)))
pca_DEseq_perc <- round(100*pca_DEseq$sdev^2/sum(pca_DEseq$sdev^2),1)
pca_DEseq_df <- data.frame(PC1 = pca_DEseq$x[,1], 
                           PC2 = pca_DEseq$x[,2], 
                           sample = colnames(assay(rld)),
                           cell.line = rep(c("SC01", "SC07", "SC10"), each = 9),
                           day = rep(c("Day0", "Day3", "Day8"), each = 3),
                           replicate = rep(c("Rep1", "Rep2", "Rep3"), times=9))

pca_DEseq_means <- ddply(pca_DEseq_df, .(cell.line, day), summarise, meanPC1 = mean(PC1), meanPC2 = mean(PC2))

ggplot(pca_DEseq_df, aes(PC1,PC2, color = cell.line))+
  geom_point(aes(shape = day), size=5) +
  geom_path(data = pca_DEseq_means, 
            aes(x=meanPC1, y=meanPC2,
                color=cell.line), arrow = arrow(),
            size = 2) +
  labs(x=paste0("PC1 (",pca_DEseq_perc[1],"% variance)"), y=paste0("PC2 (",pca_DEseq_perc[2],"% variance)")) +
  theme_bw() + ggtitle("PCA - Subclones in Time") +
  theme(legend.text = element_text(size = 12), 
        plot.title = element_text(size = 14, 
                                  hjust = 0.5, 
                                  face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        legend.position = "bottom",
        axis.title=element_text(size=12, face="bold"))
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.

NA
NA
NA

4. Differential Expression Analysis.

Always make sure to use the unnormalized raw counts for this. We will use DESeq function to perform differential analysis between samples; Unless specified, the analysis is between the last group and the first group. Different comparison can be done using ‘contrast’ argument. Steps involved underneath:

  1. estimation of size factors (controls for differences in sequencing depth of the samples)
  2. estimation of dispersion values for each gene,
  3. fitting a generalized linear model

1. Running the differential expression pipeline.

design(dds2) = ~ cell + treatment + cell:treatment
dds <- DESeq(dds2, test = "LRT", reduced = ~ cell + treatment)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
# save(dds, file = "DESeq_SC1,7,10_Timecourse_LRT.RData")
# load("DESeq_SC1,7,10_Timecourse_LRT.RData")
# dds

2. Building the results table.

By default, results will extract the estimated log2 fold changes and p values for the last variable in the design formula. If there are more than 2 levels for this variable, results will extract the results table for a comparison of the last level over the first level.

# Esimate the differences between groups by: # a) Lowering the FDR (padj) or (b) raise the log2 fold change.

resultsNames(dds)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
# alpha = FDR adjusted p value cutoff
res <- results(dds, alpha = 0.001)
summary(res)

out of 14947 with nonzero total read count
adjusted p-value < 0.001
LFC > 0 (up)       : 3918, 26%
LFC < 0 (down)     : 4378, 29%
outliers [1]       : 2, 0.013%
low counts [2]     : 290, 1.9%
(mean count < 19)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
resOrdered <- res[order(res$pvalue),]
rdata = as.data.frame(res)

Differential expression: days 0 to 8

Change significant log2 fold change to 1.585 (== 3-fold change in log2 space).

res_0to8d <- results(dds, name="treatment_8_vs_0", cooksCutoff = 0.99, 
                     independentFiltering = TRUE, alpha = 0.05, pAdjustMethod = "BH")
summary(res_0to8d)

out of 14947 with nonzero total read count
adjusted p-value < 0.05
LFC > 0 (up)       : 5581, 37%
LFC < 0 (down)     : 5275, 35%
outliers [1]       : 23, 0.15%
low counts [2]     : 0, 0%
(mean count < 10)
[1] see 'cooksCutoff' argument of ?results
[2] see 'independentFiltering' argument of ?results
# order results table by the smallest adjusted p value:
res_0to8d <- res_0to8d[order(res_0to8d$padj),]
results_0to8d <- as.data.frame(res_0to8d)

results_0to8d <- mutate(results_0to8d, sig=ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange > 1.585, "Upregulated", ifelse(results_0to8d$padj<0.05 & results_0to8d$log2FoldChange < -1.585, "Downregulated", "Not Significant")))

row.names(results_0to8d) <- row.names(res_0to8d)


head(results_0to8d)
DEgenes_0to8d <- results_0to8d[which(abs(results_0to8d$log2FoldChange) > log2(1.5) & results_0to8d$padj < 0.05),]

if(SAVEFILES) write.csv(DEgenes_0to8d, file="~/Desktop/DEgenes_0to8d.csv")

Volcano plot

volcano <- ggplot(results_0to8d, aes(log2FoldChange, -log10(pvalue))) +
  geom_point(aes(col = sig)) + theme_bw() +
  scale_color_manual(values = c("red", "grey", "green3")) +
  # ggtitle("Volcano Plot of Untreated vs Idling") +
  labs(x="log2(Fold Change)", y="Log(Odds Ratio)") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12), 
        axis.title=element_text(size=12),
        legend.position = "none")

volcano

# volcano + ggrepel::geom_text_repel(data=results_0to8d[1:10, ], 
#                                    ggplot2::aes(label=rownames(results_0to8d[1:10, ])))
# save(results_0to8d, file="untreatedIdling_DEA.RData")
DEgenes_0to8d <- DEgenes_0to8d[order(abs(DEgenes_0to8d$log2FoldChange),DEgenes_0to8d$sig, decreasing = TRUE),]
temp <- DEgenes_0to8d[DEgenes_0to8d$baseMean > 300,]
temp <- temp[abs(temp$log2FoldChange)>2,]
if(SAVEFILES) write.csv(DEgenes_0to8d, file = "DEgenes_0to8d.csv")

Generating Ion Channel Specific Gene Dataframes

test <- assay(dds)
types <- c("ATP", "TRP", "GABR", "CRACR", "SLC", "KCN", "CACN", "GRI", "ABC", "SCN", "TRP", "RIC3", "CHRND", "RYR")
samples <- c("SC01_day0", "SC01_day3", "SC01_day8", "SC07_day0", "SC07_day3", "SC07_day8", "SC10_day0", "SC10_day3", "SC10_day8")
test <- test[grep(paste(types, collapse="|"), rownames(test)),]
test1 <- sapply(samples, function(x) rowMeans(test[, grep(x, colnames(test))]))
rownames(test1) <- rownames(test)
test1 <- test1[order(rownames(test1)),]
test2 <- as.data.frame(test1)
test2["l2FC_SC01_0to8"] <- log2(test2["SC01_day8"]/test2["SC01_day0"])
test2["l2FC_SC07_0to8"] <- log2(test2["SC07_day8"]/test2["SC07_day0"])
test2["l2FC_SC10_0to8"] <- log2(test2["SC10_day8"]/test2["SC10_day0"])
test3 <- subset(test2, l2FC_SC01_0to8 > 1 & l2FC_SC07_0to8 > 1 & l2FC_SC10_0to8 > 1)
test4 <- subset(test2, l2FC_SC01_0to8 > 1 | l2FC_SC07_0to8 > 1 | l2FC_SC10_0to8 > 1)
# write.csv(x = test2, file = "all_ionChannel_Expression.csv")
# write.csv(x = test3, file = "allUpreg_ionChannel_Expression.csv")
# write.csv(x = test4, file = "atLeastOneUpreg_ionChannel_Expression.csv")
test5 <- log2(test4[, 1:9]+1)
pheatmap(test5, cluster_cols = F, cluster_rows = F)

test6 <- log2((test3[,1:9])+1)
pheatmap(test6, cluster_rows = F, cluster_cols = F)

test7 <- test5[rowSums(test5)>30,]
pheatmap(test7, cluster_rows = F, cluster_cols = F)

# load(file="untreatedIdling_DEA.RData")

OrgDB <- org.Hs.eg.db
upreg_genes <- subset(results_0to8d, padj<0.05 & log2FoldChange>2)
downreg_genes <-subset(results_0to8d, padj<0.05 & log2FoldChange<(-2))

geneList_up <- as.vector(upreg_genes$log2FoldChange)
names(geneList_up) <- rownames(upreg_genes)
geneList_down <- as.vector(downreg_genes$log2FoldChange)
names(geneList_down) <- rownames(downreg_genes)

genes_up <- as.vector(rownames(upreg_genes))
genes_down <- as.vector(rownames(downreg_genes))
# names(geneList) <- rownames(results_0to8d)
genes_up_ENTREZID <- bitr(genes_up, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:1 mapping between keys and columns
Warning: 4.56% of input gene IDs are fail to map...
genes_down_ENTREZID <- bitr(genes_down, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:1 mapping between keys and columns
Warning: 6.25% of input gene IDs are fail to map...
# Group GO
ggo_up <- clusterProfiler::groupGO(gene     = genes_up_ENTREZID,
                                OrgDb    = OrgDB,
                                ont      = "BP",
                                level    = 3,
                                readable = TRUE)
ggo_up_df <- as.data.frame(ggo_up)
ggo_up_df <- ggo_up_df[order(-ggo_up_df$Count),] 

ggo_down <- clusterProfiler::groupGO(gene = genes_down_ENTREZID,
                                OrgDb    = OrgDB,
                                ont      = "BP",
                                level    = 3,
                                readable = TRUE)
# View(as.data.frame(ggo_down))

# GO over-representation test
ego_genesUp <- clusterProfiler::enrichGO(gene  = genes_up_ENTREZID,
                                 OrgDb         = OrgDB,
                                 ont           = "BP",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)

# View(as.data.frame(ego_genesUp))

ego_genesDown <- clusterProfiler::enrichGO(gene  = genes_down_ENTREZID,
                                 OrgDb         = OrgDB,
                                 ont           = "BP",
                                 pAdjustMethod = "BH",
                                 pvalueCutoff  = 0.05,
                                 qvalueCutoff  = 0.05, 
                                 readable      = TRUE)

# View(as.data.frame(ego_genesDown))

# kk_genesUp <- enrichKEGG(gene = genes_up_ENTREZID,
#                    organism = 'hsa',
#                   pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesUp))
# 
# kk_genesDown <- enrichKEGG(gene = genes_down_ENTREZID,
#                    organism = 'hsa',
#                   pvalueCutoff = 0.05)
# View(as.data.frame(kk_genesDown))

# ego_GSEA_up <- gseGO(geneList = geneList_up,
#               OrgDb        = OrgDB,
#               ont          = "BP",
#               nPerm        = 1000,
#               minGSSize    = 100,
#               maxGSSize    = 500,
#               pvalueCutoff = 0.05,
#               verbose      = FALSE)

# barplot(ggo_up, order=T)
# barplot(ggo_down)
dotplot(ego_genesUp) + ggtitle("GO Over-representation Upregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"))


dotplot(ego_genesDown) + ggtitle("GO Over-representation Downregulated Genes") +
  labs(x="Gene Ratio", y="GO Terms") +
  theme(legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"))


# emapplot(ego_genesUp)
# emapplot(ego_genesDown)
cnetplot(ego_genesUp, categorySize="pvalue", foldChange=geneList_up)
Warning: Use 'color.params = list(foldChange = your_value)' instead of 'foldChange'.
 The foldChange parameter will be removed in the next version.Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

cnetplot(ego_genesDown, categorySize="pvalue", foldChange=geneList_down)
Warning: Use 'color.params = list(foldChange = your_value)' instead of 'foldChange'.
 The foldChange parameter will be removed in the next version.Scale for size is already present.
Adding another scale for size, which will replace the existing scale.

ego_genesUp_df <- as.data.frame(ego_genesUp) 
egoUp <- ego_genesUp_df[order(-ego_genesUp_df$Count),]
# sorted_egoUp_top10 <- head(egoUp, 10)
egoUp_genes <- strsplit(egoUp$geneID, "/", fixed=TRUE)
# egoUp_top10_genes_all <- unlist(strsplit(head(egoUp, 10)$geneID, "[/]"))
# egoUp_top10_genes_group <- strsplit(sorted_egoUp_top10$geneID, "[/]")
# egoUp_top10_genes_unique <- unique(egoUp_top10_genes)
# table(egoUp_top10_genes)
# egoUp_genesByGroup <- as.data.frame(t(plyr::ldply(egoUp_top10_genes_group, rbind)))
# colnames(egoUp_genesByGroup) <- sorted_egoUp_top10$Description
# egoUp_genesByGroup_ionOnly <- egoUp_genesByGroup[,c(1:6,8:10)]

# write.csv(egoUp_genesByGroup, file="top10GOtermsUpregulated_geneMembership.csv")
# ionGenes <- unique(unlist(egoUp_genesByGroup_ionOnly))
# 
# ensembl = useEnsembl(biomart="ensembl", dataset="hsapiens_gene_ensembl")
# IDs <- as.character(ionGenes)
# geneUpID <- names(geneList_up)
# geneDownID <- names(geneList_down)
# genedesc_ion <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = IDs, mart =ensembl)
# write.csv(genedesc_ion, file = "ionChannelGenes_description.csv")

# genedesc_Up <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneUpID, mart =ensembl)
# write.csv(genedesc_Up, file = "upregulatedGenes_description.csv")
# genedesc_Down <- getBM(attributes=c('external_gene_name','description'), filters = 'external_gene_name', values = geneDownID, mart =ensembl)
# write.csv(genedesc_Down, file = "downrgulatedGenes_description.csv")
geneList_all <- as.vector(results_0to8d$log2FoldChange)
names(geneList_all) <- rownames(results_0to8d)
a <- names(geneList_all)
genes_ENTREZID <- bitr(a, fromType = "SYMBOL", toType = "ENTREZID", OrgDb = OrgDB)$ENTREZID
'select()' returned 1:many mapping between keys and columns
Warning: 10.72% of input gene IDs are fail to map...
names(geneList_all) <- genes_ENTREZID

gene_df <- data.frame(Entrez=names(geneList_all), HGNC=a, FC=geneList_all)
gene_df <- gene_df[abs(gene_df$FC) > 1,]
gene_df$group <- "upregulated"
gene_df$group[gene_df$FC < 0] <- "downregulated"
gene_df$othergroup <- "A"
gene_df$othergroup[abs(gene_df$FC) > 2] <- "B"

formula_res <- compareCluster(Entrez~group+othergroup, data=gene_df, fun="enrichKEGG")
Warning: No enrichment found in any of gene cluster, please check your input...
head(as.data.frame(formula_res))
NA

3. Exploring Results

plotMA(res, ylim=c(-2,2))

plotCounts(dds, gene=which.min(res$padj), intgroup="treatment")

Log normalize results


# normalizedCounts <- t( t(counts(dds)) / sizeFactors(dds) )

#log2 normalized counts
rld2 <- rlog(dds, blind = FALSE)
# save(rld2, file = "RLD2_SC1,7,10_Timecourse_hmap.RData")

# load("RLD2_SC1,7,10_Timecourse_hmap.RData")

Clustering

sampleDists <- dist(t(assay(rld2)))
sampleDists
               SC01_day0_rep1 SC01_day0_rep2 SC01_day0_rep3 SC01_day3_rep1 SC01_day3_rep2 SC01_day3_rep3 SC01_day8_rep1
SC01_day0_rep2       20.65171                                                                                          
SC01_day0_rep3       18.19544       20.23140                                                                           
SC01_day3_rep1       49.40483       50.31588       48.91851                                                            
SC01_day3_rep2       49.71247       50.26344       49.82022       18.63375                                             
SC01_day3_rep3       49.76621       51.27836       49.97240       18.79982       18.39459                              
SC01_day8_rep1       62.59542       63.01800       62.85874       32.82503       30.94957       31.45619               
SC01_day8_rep2       61.72041       62.56061       61.74599       31.23421       30.60196       30.63570       18.73796
SC01_day8_rep3       61.80509       62.21007       61.82013       31.97862       30.72020       31.16024       18.45084
SC07_day0_rep1       81.89472       82.20333       81.04736       88.58433       89.21057       89.31203       92.42884
SC07_day0_rep2       81.81217       82.21052       81.28982       88.75768       89.12405       89.21787       92.00740
SC07_day0_rep3       81.88056       81.87696       80.69148       88.82906       89.71839       89.75197       93.26978
SC07_day3_rep1       85.61380       85.99181       84.75651       72.67150       73.42118       73.65364       73.24722
SC07_day3_rep2       86.14718       85.54792       85.05149       73.05656       73.23269       73.98284       73.12226
SC07_day3_rep3       85.65039       86.08172       85.21571       72.75545       72.78011       73.15645       71.75705
SC07_day8_rep1       80.85774       80.83407       79.81235       68.49272       69.17010       69.63938       69.64941
SC07_day8_rep2       81.78651       81.86365       81.53807       68.99423       68.66122       69.49822       67.68127
SC07_day8_rep3       81.96379       82.97083       82.07809       69.32223       69.04497       69.60840       67.94316
SC10_day0_rep1       83.08483       83.00857       82.09658       91.38963       92.06252       92.15414       95.59004
SC10_day0_rep2       82.31343       82.42280       81.53200       90.68658       91.20490       91.27712       94.48998
SC10_day0_rep3       83.39163       83.06685       81.97586       90.84027       91.32067       91.65829       94.96716
SC10_day3_rep1       94.56538       95.04695       94.07956       78.96682       79.09632       79.16897       77.52321
SC10_day3_rep2       94.54772       94.57592       93.82512       78.91635       79.25387       79.40452       77.96074
SC10_day3_rep3       93.64543       93.44949       92.79417       78.81764       79.42600       79.33981       78.08186
SC10_day8_rep1       87.28001       86.98749       86.05702       69.06667       69.89089       69.80170       65.85874
SC10_day8_rep2       87.47985       86.48166       86.26001       69.46186       69.73044       70.05186       65.80912
SC10_day8_rep3       86.88712       86.54817       85.75162       68.90153       69.55335       69.66973       65.74204
               SC01_day8_rep2 SC01_day8_rep3 SC07_day0_rep1 SC07_day0_rep2 SC07_day0_rep3 SC07_day3_rep1 SC07_day3_rep2
SC01_day0_rep2                                                                                                         
SC01_day0_rep3                                                                                                         
SC01_day3_rep1                                                                                                         
SC01_day3_rep2                                                                                                         
SC01_day3_rep3                                                                                                         
SC01_day8_rep1                                                                                                         
SC01_day8_rep2                                                                                                         
SC01_day8_rep3       18.70173                                                                                          
SC07_day0_rep1       91.45962       91.76440                                                                           
SC07_day0_rep2       91.40712       91.45839       18.52529                                                            
SC07_day0_rep3       92.11601       92.37973       19.01497       20.12043                                             
SC07_day3_rep1       72.19417       72.59749       54.91862       55.10282       54.94104                              
SC07_day3_rep2       72.53967       72.64952       55.77708       55.88030       56.04933       20.64862               
SC07_day3_rep3       71.51446       71.58988       55.65198       55.01198       56.47730       20.64704       21.58226
SC07_day8_rep1       68.65439       68.66252       66.70252       67.10961       66.19657       37.28210       37.68683
SC07_day8_rep2       67.76158       67.38921       68.72697       68.18688       69.23242       39.26255       38.47638
SC07_day8_rep3       67.80631       67.67859       68.79983       68.20694       69.42419       38.93224       39.82064
SC10_day0_rep1       94.47595       94.69824       52.02540       52.36629       51.90081       74.68353       75.29809
SC10_day0_rep2       93.51165       93.71597       51.61810       51.84961       51.64881       74.14492       74.80949
SC10_day0_rep3       93.98204       94.03740       53.25482       53.88450       53.20057       74.39264       74.38328
SC10_day3_rep1       76.83178       77.16526       70.16111       69.88900       70.85687       48.49477       48.88269
SC10_day3_rep2       77.23468       77.40031       70.19613       70.01563       70.35796       48.20663       48.52571
SC10_day3_rep3       77.22063       77.38268       70.35089       70.17405       70.11908       49.54113       50.52511
SC10_day8_rep1       64.53630       64.95505       72.74623       72.51292       72.29412       52.32687       53.63736
SC10_day8_rep2       64.74893       64.98983       72.83182       72.54879       72.38364       53.04593       53.43402
SC10_day8_rep3       64.46140       64.89114       72.30221       72.17670       72.01685       52.43969       53.46083
               SC07_day3_rep3 SC07_day8_rep1 SC07_day8_rep2 SC07_day8_rep3 SC10_day0_rep1 SC10_day0_rep2 SC10_day0_rep3
SC01_day0_rep2                                                                                                         
SC01_day0_rep3                                                                                                         
SC01_day3_rep1                                                                                                         
SC01_day3_rep2                                                                                                         
SC01_day3_rep3                                                                                                         
SC01_day8_rep1                                                                                                         
SC01_day8_rep2                                                                                                         
SC01_day8_rep3                                                                                                         
SC07_day0_rep1                                                                                                         
SC07_day0_rep2                                                                                                         
SC07_day0_rep3                                                                                                         
SC07_day3_rep1                                                                                                         
SC07_day3_rep2                                                                                                         
SC07_day3_rep3                                                                                                         
SC07_day8_rep1       38.90824                                                                                          
SC07_day8_rep2       37.57388       23.11737                                                                           
SC07_day8_rep3       37.48819       23.93251       18.61309                                                            
SC10_day0_rep1       75.55978       80.35932       82.91537       83.35237                                             
SC10_day0_rep2       74.65244       79.78211       81.98123       82.22876       17.57831                              
SC10_day0_rep3       75.28901       79.44072       82.35629       82.91009       22.39798       22.72279               
SC10_day3_rep1       47.75866       61.98942       61.90329       61.74289       66.19934       65.55259       66.50108
SC10_day3_rep2       47.95099       61.58948       62.34238       62.54834       65.91055       65.30386       65.89761
SC10_day3_rep3       49.87898       62.12438       63.49194       63.64168       65.62650       65.12926       66.27753
SC10_day8_rep1       52.63151       59.08839       60.79552       60.88443       67.11258       66.51853       67.47053
SC10_day8_rep2       53.03463       59.52660       60.74246       61.37573       67.24014       66.69203       67.60283
SC10_day8_rep3       52.80719       59.01986       60.62481       60.88795       66.48904       65.97280       66.65307
               SC10_day3_rep1 SC10_day3_rep2 SC10_day3_rep3 SC10_day8_rep1 SC10_day8_rep2
SC01_day0_rep2                                                                           
SC01_day0_rep3                                                                           
SC01_day3_rep1                                                                           
SC01_day3_rep2                                                                           
SC01_day3_rep3                                                                           
SC01_day8_rep1                                                                           
SC01_day8_rep2                                                                           
SC01_day8_rep3                                                                           
SC07_day0_rep1                                                                           
SC07_day0_rep2                                                                           
SC07_day0_rep3                                                                           
SC07_day3_rep1                                                                           
SC07_day3_rep2                                                                           
SC07_day3_rep3                                                                           
SC07_day8_rep1                                                                           
SC07_day8_rep2                                                                           
SC07_day8_rep3                                                                           
SC10_day0_rep1                                                                           
SC10_day0_rep2                                                                           
SC10_day0_rep3                                                                           
SC10_day3_rep1                                                                           
SC10_day3_rep2       18.41568                                                            
SC10_day3_rep3       25.81500       23.92748                                             
SC10_day8_rep1       38.31173       36.56576       37.42276                              
SC10_day8_rep2       39.03488       37.15372       37.99527       19.07979               
SC10_day8_rep3       38.31050       36.50575       37.73213       17.66540       18.42072
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste(rld2$treatment, rld2$cell, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
         clustering_distance_rows = sampleDists,
         clustering_distance_cols = sampleDists,
         col = colors)



poisd <- PoiClaClu::PoissonDistance(t(counts(dds)))
samplePoisDistMatrix <- as.matrix( poisd$dd )
rownames(samplePoisDistMatrix) <- paste( dds$dex, dds$cell, sep=" - " )
colnames(samplePoisDistMatrix) <- NULL
pheatmap(samplePoisDistMatrix,
         clustering_distance_rows = poisd$dd,
         clustering_distance_cols = poisd$dd,
         col = colors)


mds <- as.data.frame(colData(rld2))  %>%
         cbind(cmdscale(sampleDistMatrix))
ggplot(mds, aes(x = `1`, y = `2`, color = cell, shape = treatment)) +
  geom_point(size = 3) + coord_fixed() + theme_bw() +
  xlab("PC1") + ylab("PC2") +
  theme(legend.text = element_text(size = 10), 
        plot.title = element_text(size = 14, 
                                  hjust = 0.5, 
                                  face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"),
        # legend.position = "bottom",
        axis.title=element_text(size=12))


# library("genefilter")
topVarGenes <- head(order(rowVars(assay(rld2)), decreasing = TRUE), 5000)
Warning: useNames = NA is deprecated. Instead, specify either useNames = TRUE or useNames = TRUE.
mat  <- assay(rld2)[ topVarGenes, ]
mat  <- mat - rowMeans(mat)
anno <- as.data.frame(colData(rld2)[, c("cell","treatment")])
names(anno) <- c("Cell", "Treatment")
annotation_colors = list(
  Cell = c(SC01="red2", SC07="green2", SC10="blue2"),
  Treatment = c("0"="cyan2", "3"="darkorange", "8"="darkorchid"))
pheatmap(mat, annotation_col = anno, show_rownames = F, show_colnames = F,
         annotation_colors = annotation_colors)

Time series analysis

1 DESeq2 time series analysis

# browseVignettes("rnaseqGene")

ddsTC <- DESeq(dds, test="LRT", reduced = ~ cell + treatment)
using pre-existing size factors
estimating dispersions
found already estimated dispersions, replacing these
gene-wise dispersion estimates
mean-dispersion relationship
final dispersion estimates
fitting model and testing
resTC <- results(ddsTC)
resTC$symbol <- mcols(ddsTC)$symbol
# head(resTC[order(resTC$padj),], 4)

tc <- plotCounts(ddsTC, which.min(resTC$padj), 
                   intgroup = c("treatment","cell"), returnData = TRUE)

ddsTC[which.min(resTC$padj),]
class: DESeqDataSet 
dim: 1 27 
metadata(1): version
assays(4): counts mu H cooks
rownames(1): PDK4
rowData names(35): baseMean baseVar ... deviance maxCooks
colnames(27): SC01_day0_rep1 SC01_day0_rep2 ... SC10_day8_rep2 SC10_day8_rep3
colData names(4): cell treatment group sizeFactor
ggplot(tc,
  aes(x = rep(c(0,3,8), each=9), y = count, color = cell, group = cell)) + 
  geom_point() + geom_smooth(se = FALSE, method = "loess") + scale_y_log10() +
  theme_bw() +
  ggtitle("Time Course Expression of PDK4") +
  labs(x="Time (days)", y="Gene Count") +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.text = element_text(size = 12),
        plot.title = element_text(size = 14, hjust = 0.5, face = "bold"), 
        axis.text=element_text(size=12),
        legend.title = element_text(size=12,face="bold"), 
        axis.title=element_text(size=12, face="bold"),
        legend.position = "bottom")


resultsNames(ddsTC)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
betas <- coef(ddsTC)
colnames(betas)
[1] "Intercept"           "cell_SC07_vs_SC01"   "cell_SC10_vs_SC01"   "treatment_3_vs_0"    "treatment_8_vs_0"   
[6] "cellSC07.treatment3" "cellSC10.treatment3" "cellSC07.treatment8" "cellSC10.treatment8"
topGenes <- head(order(resTC$padj),50)
mat <- betas[topGenes, -c(1,2)]
thr <- 3 
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
pheatmap(mat, breaks=seq(from=-thr, to=thr, length=101),
         cluster_col=FALSE)

NOTE

Original code below produced many messages of No id variables; using all as measure variables; presumably a line for each gene. This is due to the melt function not having any id variables to use.

Rejiggering code not yet finished. Should probably use

2. ANOVA btwn time points & shared btwn sublines)

group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC01 <- list()
TukeySC01_time0 <- list()
TukeySC01_time3 <- list()
TukeySC01_time8 <- list()
norm_data_SC01time <- as.data.frame(assay(rld2))[c('SC01_day0_rep1',
                                        'SC01_day0_rep2',
                                        'SC01_day0_rep3',
                                        'SC01_day3_rep1',
                                        'SC01_day3_rep2',
                                        'SC01_day3_rep3',
                                        'SC01_day8_rep1',
                                        'SC01_day8_rep2',
                                        'SC01_day8_rep3')]
for (gene in 1:nrow(norm_data_SC01time)) {
  gene_norm_data <- norm_data_SC01time[gene,]
  # d3 <- data.frame(y = gene_norm_data, group = group)
  # fit <- lm(y~group, d3)
  # gene_norm_data_melt <- melt(gene_norm_data)
  gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
                                    value=as.numeric(t(gene_norm_data)))

  gene_norm_data_melt$group <- group
  fit <- aov(value~group, gene_norm_data_melt)
  # anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
  anova_SC01[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
  results <- TukeyHSD(fit, conf.level = 0.95)
  TukeySC01_time0[gene] <- results$group[,'p adj'][1]
  TukeySC01_time3[gene] <- results$group[,'p adj'][2]
  TukeySC01_time8[gene] <- results$group[,'p adj'][3]
  
}
# print(anova_list)
anova_SC01_pval <- unlist(anova_SC01) # make array
TukeySC01_time0_pval <- unlist(TukeySC01_time0)
TukeySC01_time3_pval <- unlist(TukeySC01_time3)
TukeySC01_time8_pval <- unlist(TukeySC01_time8)

# Make master dataset with statistics
norm_data_stats_SC01 <- as.data.frame(norm_data_SC01time)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, anova_SC01_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time0_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time3_pval)
norm_data_stats_SC01 <- cbind(norm_data_stats_SC01, TukeySC01_time8_pval)

# save(norm_data_stats_SC01, file = "subcloneCounts_anova_tukey_DESeq2_SC01time.RData")

# Identify genes that differ between clones based on 
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC01_pval < sigThresh)
table(TukeySC01_time0_pval < sigThresh)
table(TukeySC01_time3_pval < sigThresh)
table(TukeySC01_time8_pval < sigThresh)

sigIndecies_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh)

sigIndeciesAll_SC01 <- which(norm_data_stats_SC01["anova_SC01_pval"] < sigThresh & 
                          norm_data_stats_SC01["TukeySC01_time0_pval"] < sigThresh &
                          norm_data_stats_SC01["TukeySC01_time3_pval"] < sigThresh &
                          norm_data_stats_SC01["TukeySC01_time8_pval"] < sigThresh)

sigDiffGenes_SC01 <- rownames(norm_data_stats_SC01[sigIndecies_SC01,])
sigDiffGenesAll_SC01 <- rownames(norm_data_stats_SC01[sigIndeciesAll_SC01,])
group<-as.factor(c(1,1,1,2,2,2,3,3,3))
# Getting anova values for each gene in dataset
anova_SC07 <- list()
TukeySC07_time0 <- list()
TukeySC07_time3 <- list()
TukeySC07_time8 <- list()
norm_data_SC07time <- as.data.frame(assay(rld2))[c('SC07_day0_rep1',
                                        'SC07_day0_rep2',
                                        'SC07_day0_rep3',
                                        'SC07_day3_rep1',
                                        'SC07_day3_rep2',
                                        'SC07_day3_rep3',
                                        'SC07_day8_rep1',
                                        'SC07_day8_rep2',
                                        'SC07_day8_rep3')]
for (gene in 1:nrow(norm_data_SC07time)) {
  gene_norm_data <- norm_data_SC07time[gene,]
  # d3 <- data.frame(y = gene_norm_data, group = group)
  # fit <- lm(y~group, d3)
  # gene_norm_data_melt <- melt(gene_norm_data)
  gene_norm_data_melt <- data.frame(variable=colnames(gene_norm_data),
                                    value=as.numeric(t(gene_norm_data)))
  gene_norm_data_melt$group <- group
  fit <- aov(value~group, gene_norm_data_melt)
  # anova_list[gene] <- anova(fit)$'Pr(>F)'[1]
  anova_SC07[gene] <- summary(fit)[[1]][['Pr(>F)']][1]
  results <- TukeyHSD(fit, conf.level = 0.95)
  TukeySC07_time0[gene] <- results$group[,'p adj'][1]
  TukeySC07_time3[gene] <- results$group[,'p adj'][2]
  TukeySC07_time8[gene] <- results$group[,'p adj'][3]
  
}
# print(anova_list)
anova_SC07_pval <- unlist(anova_SC07) # make array
TukeySC07_time0_pval <- unlist(TukeySC07_time0)
TukeySC07_time3_pval <- unlist(TukeySC07_time3)
TukeySC07_time8_pval <- unlist(TukeySC07_time8)

# Make master dataset with statistics
norm_data_stats_SC07 <- as.data.frame(norm_data_SC07time)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, anova_SC07_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time0_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time3_pval)
norm_data_stats_SC07 <- cbind(norm_data_stats_SC07, TukeySC07_time8_pval)

# save(norm_data_stats_SC07, file = "subcloneCounts_anova_tukey_DESeq2_SC07time.RData")

# Identify genes that differ between clones based on 
# ANOVA p-value with defined threshold
sigThresh <- 0.05
table(anova_SC07_pval < sigThresh)
table(TukeySC07_time0_pval < sigThresh)
table(TukeySC07_time3_pval < sigThresh)
table(TukeySC07_time8_pval < sigThresh)

sigIndecies_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh)

sigIndeciesAll_SC07 <- which(norm_data_stats_SC07["anova_SC07_pval"] < sigThresh & 
                          norm_data_stats_SC07["TukeySC07_time0_pval"] < sigThresh &
                          norm_data_stats_SC07["TukeySC07_time3_pval"] < sigThresh &
                          norm_data_stats_SC07["TukeySC07_time8_pval"] < sigThresh)

sigDiffGenes_SC07 <- rownames(norm_data_stats_SC07[sigIndecies_SC07,])
sigDiffGenesAll_SC07 <- rownames(norm_data_stats_SC07[sigIndeciesAll_SC07,])

3 Jack’s method

#Grab all the names from res in the DESeq matrix
topGenes <- which(res$padj <= 0.001)

countMAT <- data.frame(normalizedCounts[topGenes,])

subrl = data.frame(assay(rld2))
rlMAT = data.frame(subrl[topGenes,])

#Labeling rows with ENSG IDs
# countMAT$ensembl_gene_id = row.names(countMAT)
# countMAT$padj = res[topGenes,"padj"]

rlMAT$ensembl_gene_id = row.names(rlMAT)
rlMAT$padj = res[topGenes,"padj"]

# library(biomaRt)
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(rlMAT)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
#                 filters= "ensembl_gene_id",
#                 values=genes,
#                 mart=mart)

#Check if data fits a normal distribution
# plot(density(c(as.matrix(countMAT[,1:27]))))
plot(density(c(as.matrix(rlMAT[,1:27]))))


#rlMAT follows a normal distribution, therefore we will use this in the heatmap construction
#Labeling df with hgnc symbols
GE_data <- merge(G_list, rlMAT, by = "ensembl_gene_id")

#Making rownames unique hgnc symbols
rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
GE_data = GE_data[order(GE_data$padj),]


#Averaging rld between trials
Acol <- c("SC01_day0",
          "SC01_day3",
          "SC01_day8",
          "SC07_day0",
          "SC07_day3",
          "SC07_day8",
          "SC10_day0",
          "SC10_day3",
          "SC10_day8")
for(i in 1:length(Acol)){
  j = 2+i
  k = 2+3*i
  GE_data[,Acol[i]] = rowMeans(GE_data[,c(j:k)])
}


#Calculating fold changes across conditions in a triangular matrix form
GE_mean = GE_data[,c(1,2,30:39)]
DEProc = GE_mean
startcol = 4
endcol = 12

allFC <- function(DEProc,startcol,endcol){ 
  GE_fold = DEProc[,-c(startcol:endcol)]
  colvec = colnames(DEProc)[startcol:endcol]
  
  #Last index is a self comparison and is removed
  for(k in 1:(length(colvec)-1)){
    #Start with column that is 1 away from index 
    for(j in (k+1):length(colvec)){
      compnam = paste0(colvec[j],"/",colvec[k])
      #Loop through each gene/row  
      for(i in 1:nrow(DEProc)){
        f = DEProc[i,colvec[j]]
        h = DEProc[i,colvec[k]]
        
        #Capture upregulation and down regulation
        if(f>h){
          GE_fold[i,compnam] = 2^(f-h)
        }else{
          GE_fold[i,compnam] = -2^(h-f)
        }
        
      }
    }
  }
  
  return(GE_fold)
  
}

#Subset gene, then plot, then save plot
#Perhaps make heatmaps with scaled z scores
#Is there a way to consolidate replicate z scores? Geometric mean? 
#Regular mean, then scale.

# ImpRat = colnames(GE_fold)[c(4,5,6,9,12,14,17,21,24,25,26,27,30,32,36,37,38,39)]

#Listing of all important comparisons?
ImpRat = c("SC01_day3/SC01_day0", "SC01_day8/SC01_day3", "SC01_day8/SC01_day0", 
           "SC07_day3/SC07_day0", "SC07_day8/SC07_day3", "SC07_day8/SC07_day0", 
           "SC10_day3/SC10_day0", "SC10_day8/SC10_day3", "SC10_day8/SC10_day0", 
           "SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0",
           "SC07_day3/SC01_day3", "SC10_day3/SC01_day3", "SC10_day3/SC07_day3",
           "SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8" )
Imp_fold = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", ImpRat)]
Imp_fold2 = Imp_fold[rowSums(abs(Imp_fold[,4:21])>=1.5)>=1,]

# write.table(Imp_fold,"SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t", row.names=F)

Imp_fold = read.delim("SC1,7,10-TimecoursePLX-ImportantFC_20180722.txt", sep="\t")

#Subset the LF mean of important genes from Log2 Fold Change (LFC) comparison data frame.
GE_Imp = subset(GE_mean,GE_mean$ensembl_gene_id%in%Imp_fold2$ensembl_gene_id)

Necro = read.delim("KEGGNecroptosis_hsa04217_06-25-18.txt", header=T, stringsAsFactors = F)
Necro = Necro[rowSums(is.na(Necro)) == 0, ]
DE_Necro = merge(GE_Imp, Necro, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Necro) = make.names(DE_Necro[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Necro[3:29],cluster_cols = TRUE)
# write.table(DE_Necro, "KEGGNecroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)


Apop = read.delim("KEGGApoptosis_hsa04210_06-25-18.txt", header=T, stringsAsFactors = F)
Apop = Apop[rowSums(is.na(Apop)) == 0, ]
DE_Apop = merge(GE_Imp), Apop, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Apop) = make.names(DE_Apop[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Apop[3:29],cluster_cols = TRUE, scale = "row")
# write.table(DE_Apop, "KEGGApoptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)

Ferr = read.delim("KEGGFerroptosis_hsa04216_06-25-18.txt", header=T, stringsAsFactors = F)
Ferr = Ferr[rowSums(is.na(Ferr)) == 0, ]
DE_Ferr = merge(GE_Imp, Ferr, by.x = "hgnc_symbol", by.y = "GeneName")
row.names(DE_Ferr) = make.names(DE_Ferr[,"hgnc_symbol"], unique = TRUE)
pheatmap(DE_Ferr[4:12],cluster_cols=FALSE, scale = "row")
# write.table(DE_Ferr, "KEGGFerroptosis SC1,7,10 DESeq LRT.txt", sep="\t", row.names=FALSE, quote=FALSE)

4. Different LC comparisons. Between subclones and at baseline vs idling.

Zscore heatmaps of relevant comparisons can be made as in above to visualize.

#USES ABOVE CODE TO LINE 280. Run that pseudo-function.

# library(pheatmap)
#Comparisons of difEx between subclones at baseline and idling
BetweenBase = c("SC07_day0/SC01_day0", "SC10_day0/SC01_day0", "SC10_day0/SC07_day0")
BetweenIdle = c("SC07_day8/SC01_day8", "SC10_day8/SC01_day8", "SC10_day8/SC07_day8")
 

#Unsure of how strict to make the cutoff. Should all the genes between clones be differentially expressed (3) or is a single difference sufficient?
Btw_b = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenBase)]
Btw_b1 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=1,]
Btw_b2 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=2,]
Btw_b3 = Btw_b[rowSums(abs(Btw_b[,4:6])>=1.5)>=3,]

Btw_i = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", BetweenIdle)]
Btw_i1 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=1,]
Btw_i2 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=2,]
Btw_i3 = Btw_i[rowSums(abs(Btw_i[,4:6])>=1.5)>=3,]

#This does not account for same direction of change. This can be plotted in a heatmap to view.
#Members that were "lost" by the baseline condition at being different. Were no longer found diffEx between the subclones when comparing baseline to idling DEGs.
LostDEG_b_1 = subset(Btw_b1,!Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
LostDEG_b_2 = subset(Btw_b2,!Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
LostDEG_b_3 = subset(Btw_b3, !Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)

##Make heatmap
LostDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_3$ensembl_gene_id)
row.names(LostDEG_b_3_mean) = make.names(LostDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")

#Members that remained different. 
StaticDEG_b_1 = subset(Btw_b1,Btw_b1$ensembl_gene_id%in%Btw_i1$ensembl_gene_id)
StaticDEG_b_2 = subset(Btw_b2,Btw_b2$ensembl_gene_id%in%Btw_i2$ensembl_gene_id)
StaticDEG_b_3 = subset(Btw_b3, Btw_b3$ensembl_gene_id%in%Btw_i3$ensembl_gene_id)

##Some HGNC_symbols are duplicates! I switched to ensembl_gene_id to fix.
StaticDEG_i_3 = subset(Btw_i3, Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)


##Make heatmap
StaticDEG_b_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%StaticDEG_b_3$ensembl_gene_id)
row.names(StaticDEG_b_3_mean) = make.names(StaticDEG_b_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(StaticDEG_b_3_mean[4:12],cluster_cols=FALSE, scale = "row")


#Members that "gained" differences between the subclones in idling.  
GainDEG_i_1 = subset(Btw_i1, !Btw_i1$ensembl_gene_id%in%Btw_b1$ensembl_gene_id)
GainDEG_i_2 = subset(Btw_i2, !Btw_i2$ensembl_gene_id%in%Btw_b2$ensembl_gene_id)
GainDEG_i_3 = subset(Btw_i3, !Btw_i3$ensembl_gene_id%in%Btw_b3$ensembl_gene_id)

##Make heatmap
GainDEG_i_3_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%GainDEG_i_3$ensembl_gene_id)
row.names(GainDEG_i_3_mean) = make.names(GainDEG_i_3_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(GainDEG_i_3_mean[4:12],cluster_cols=FALSE, scale = "row")


#Members that were differentially expressed between idling (8day) and baseline within subclones. Those with shared diffEx may be convergent across multiple subclones depending on direction of expresison change.
Endpoint = c("SC01_day8/SC01_day0", "SC07_day8/SC07_day0", "SC10_day8/SC10_day0")
BtoIdle = GE_fold[,c("ensembl_gene_id", "hgnc_symbol", "padj", Endpoint)]
BtoIdle_1 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=1,]
BtoIdle_2 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=2,]
BtoIdle_3 = BtoIdle[rowSums(abs(BtoIdle[,4:6])>=1.5)>=3,]

##Make heatmap
BtoIdle_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%BtoIdle_2$ensembl_gene_id)
row.names(BtoIdle_2_mean) = make.names(BtoIdle_2_mean[,"hgnc_symbol"], unique = TRUE)

BtoIdle_2_mean_incExp = BtoIdle_2_mean[which(BtoIdle_2_mean$SC01_day0 < BtoIdle_2_mean$SC01_day8),]
BtoIdle_2_mean_incExp = BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC07_day0 < BtoIdle_2_mean_incExp$SC07_day8),]
BtoIdle_2_mean_incExp[which(BtoIdle_2_mean_incExp$SC10_day0 < BtoIdle_2_mean_incExp$SC10_day8),]

LostDEG_b_2_mean = subset(GE_mean,GE_mean$ensembl_gene_id%in%LostDEG_b_2$ensembl_gene_id)
row.names(LostDEG_b_2_mean) = make.names(LostDEG_b_2_mean[,"hgnc_symbol"], unique = TRUE)
pheatmap(LostDEG_b_2_mean[4:12],cluster_cols=FALSE, scale = "row")

BtoIdleIncExp_DEbetweenSCs = BtoIdle_2_mean_incExp[which(row.names(BtoIdle_2_mean_incExp) %in% row.names(LostDEG_b_2_mean)),]

pheatmap(BtoIdle_2_mean_incExp[4:12],cluster_cols=FALSE, scale = "row")

# library(devtools)
# # install_github("wjawaid/enrichR")
# library(enrichR)
dbs <- listEnrichrDbs()
head(dbs)
dbs <- c("GO_Molecular_Function_2015", "GO_Cellular_Component_2015", "GO_Biological_Process_2015")

enriched <- enrichr(row.names(BtoIdle_2_mean_incExp), dbs)

View(enriched[["GO_Molecular_Function_2015"]])
View(enriched[["GO_Cellular_Component_2015"]])
View(enriched[["GO_Biological_Process_2015"]])

enriched_MF_sig <- enriched[["GO_Molecular_Function_2015"]][enriched[["GO_Molecular_Function_2015"]]$Adjusted.P.value<0.05,]
enriched_MF_sig_df <- data.frame(enriched_MF_sig[,c(1,4,9)])
write.csv(enriched_MF_sig_df, "enriched_MF_significant.csv")

enriched_BP_sig <- enriched[["GO_Biological_Process_2015"]][enriched[["GO_Biological_Process_2015"]]$Adjusted.P.value<0.05,]
enriched_BP_sig_df <- data.frame(enriched_BP_sig[,c(1,4,9)])
# write.csv(enriched_BP_sig_df, "enriched_BP_significant.csv")

Gini_scGenes <- c("APOE", "BCAN", "CES1", "CITED1",
                  "CPM", "CTSF", "DCT", "EDNRB", 
                  "EGR1", "ESRP1", "FSTL1", "MALAT1",
                  "MAP2K6", "MCF2L", "MLANA", "MXD4",
                  "OCA2", "PMEL", "SEMA6A", "SNAI2",
                  "SOX4", "TSPAN10")
enriched_sc <- enrichr(Gini_scGenes, dbs)

row.names(BtoIdle_2_mean_incExp) %in% Gini_scGenes

Rest of Jack’s Analysis

#Visually inspect trending members from heatmaps.
#Plots of specific trending members?
p <- ggplot(data=df2, aes(x=dose, y=len, fill=supp)) +
  geom_bar(stat="identity", color="black", position=position_dodge())+
  theme_minimal()

NOTE: code below reuses object names… WILL OVERWRITE!

#GLM Coef Heatmap.
betas <- coef(dds)
topGenes <- which(res$padj <= 0.001)
mat <- data.frame(betas[topGenes,])
mat$ensembl_gene_id = row.names(mat)
mat$padj = res[topGenes,"padj"]
# ensembl <- useMart("ensembl")
# mart <- useDataset("hsapiens_gene_ensembl", mart = ensembl)
# genes = row.names(mat)
# G_list <- getBM(attributes= c("ensembl_gene_id","hgnc_symbol"),
#                 filters= "ensembl_gene_id",
#                 values=genes,
#                 mart=mart)

# GE_data <- merge(mat, G_list, by = "ensembl_gene_id")
# rownames(GE_data) <- make.names(GE_data[,"hgnc_symbol"], unique = TRUE)
# GE_data = GE_data[order(GE_data$padj),]


#Sorting script to pick out entries greater than or less than +-1
eg = c()
for(i in 3:10){
  g = which(GE_data[,i] > 3 | GE_data[,i] < -3)
  eg = c(eg,g)
}
eg = unique(eg)

mat = GE_data[eg,-c(1:2,11,12)]
thr <- 3 
mat[mat < -thr] <- -thr
mat[mat > thr] <- thr
# library(pheatmap)
pheatmap(mat, cluster_cols = FALSE)

# ssdg = sdg[1:1000, ]
dim(sdg)
head(sdg)
LS0tCnRpdGxlOiAiUk5Bc2VxIERFU2VxMiBUaW1lIENvdXJzZSIKYXV0aG9yOiAiQ29yZXkgSGF5Zm9yZCAobW9kaWZlZCBmcm9tIEphY2spIgpkYXRlOiAiTm92ZW1iZXIgMjAxOC1KYW51YXJ5IDIwMTkiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyBEUlQgbW9kaWZpZWQgZmlsZSB0byB3b3JrIG9uIGFueSBjb21wdXRlcgoyMDIyLTAxLTE3ClNvbWUgZmlsZXMgYXBwZWFyZWQgdG8gYmUgbWlzc2luZyB0byBydW4gdGhpcyBub3RlYm9vay4gVHJ5aW5nIHRvIGdldCBhbGwgdG8gcnVuCgojIyBPdmVydmlldwpUaGlzIGlzIGEgcGlwZWxpbmUgZm9yIGRpZmZlcmVudGlhbCBhbmFseXNpcyBvZiBSTkFTZXEgZGF0YSBmcm9tIFNLTUVMNSBzdWJsaW5lcyB1c2luZyBERVNlcTIgc3RhdGlzdGljYWwgcGFja2FnZS4gVGhyZWUgc3VibGluZXM6IFNDMDEgKCpyZWdyZXNzaW5nKiksIFNDMDcgKCpzdGF0aW9uYXJ5KikgYW5kIFNDMTAgKCpleHBhbmRpbmcqKSB3ZXJlIGFuYWx5emVkIGZvciBnZW5lIGV4cHJlc3Npb24gZGlmZmVyZW5jZXMuIEluIGFkZGl0aW9uLCB0aW1lIGNvdXJzZSBjaGFuZ2VzIGluIDh1TSBQTFg0NzIwIHdlcmUgYWxzbyBwZXJmb3JtZWQgZm9yIGVhY2ggc3VibGluZS4gVGltZSBwb2ludHMgYXJlOiAwLCAzZCwgOGQuIFRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgd2lsbCBiZSBwZXJmb3JtZWQgYmFzZWQgb24gdGhlIGNvbnRyYXN0cyBkZWZpbmVkIGJlbG93LiAKR2VuZXJhbCBzdGVwcyBmb3IgdGhlIGFuYWx5c2lzIGFyZToKICAKIyMjMS4gUmVhZCBjb3VudHMgdGFibGU6IAorIENvdWxkIGJlIHJlYWQgZGlyZWN0bHkgYXMgYSBjc3YvdHh0IGZpbGUuIAorIEFsaWdubWVudCBhbmQgcmVhZCBjb3VudHMgY291bGQgYmUgZG9uZSB3aXRoaW4gUiBlbnZpcm9ubWVudCB0byBjcmVhdGUgcmVhZCBjb3VudHMgdGFibGUuIAoxLiBEZWZpbmUgd29ya2luZyBkaXJlY3RvcnksIGxvYWQgdGhlIHJlcXVpcmVkIGxpYnJhcmllcy4gCjIuIEdldCByZWFkIGNvdW50cyB0YWJsZS4gClJlYWQgdGhlIHJhdyBjb3VudHMgZmlsZSBwcm9jZXNzZWQgYnkgZmVhdHVyZUNvdW50cy4gVGhlIGZhc3RxIGZpbGVzIHdlcmUgYWxpZ25lZCB3aXRoIEhpU2F0MiwgYW5kIHRoZSByZWFkIGNvdW50cyB3ZXJlIG9idGFpbmVkIHVzaW5nIGZlYXR1cmVDb3VudHMgb2YgUnN1YnJlYWQgcGFja2FnZXMuCgpgYGB7ciBJbnN0YWxsYXRpb24sIGV2YWw9RkFMU0V9CnBrZ3MgPC0gYygiREVTZXEyIiwib3JnLkhzLmVnLmRiIiwiY2x1c3RlclByb2ZpbGVyIiwiSERPLmRiIiwKICAgICAgICAgICJwaGVhdG1hcCIsImdnbmV3c2NhbGUiLCJQb2lDbGFDbHUiLCJlbnJpY2hSIiwiZ3RhYmxlIiwiUm1pc2MiKQpzb3VyY2UoImdldFJlcWRQa2dzLnIiKQpnZXRSZXFkUGtncyhwa2dzKQpgYGAKCgpgYGB7ciBTZXR1cH0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGV4cHI9ewogICAgbGlicmFyeShwbHlyKQogICAgbGlicmFyeShkcGx5cikKICAgIGxpYnJhcnkoZ2dwbG90MikKICAgIGxpYnJhcnkoZ2duZXdzY2FsZSkKICAgIGxpYnJhcnkocmVzaGFwZTIpCiAgICBsaWJyYXJ5KERFU2VxMikKICAgIGxpYnJhcnkoZ2dyZXBlbCkKICAgIGxpYnJhcnkocGhlYXRtYXApCiAgICBsaWJyYXJ5KG9yZy5Icy5lZy5kYikKICAgIGxpYnJhcnkoY2x1c3RlclByb2ZpbGVyKQogICAgbGlicmFyeSgiUkNvbG9yQnJld2VyIikKICAgIGxpYnJhcnkoZW5yaWNoUikKfSkKClNBVkVGSUxFUyA8LSBGQUxTRQpgYGAKCmBgYHtyLCBlY2hvPVRSVUV9CmxpYnJhcnkoYmlvbWFSdCkKZCA8LSByZWFkLmNzdigiLi4vZGF0YS9mZWF0dXJlQ291bnRzX21hdHJpeF9hbGwuY3N2IiwgaGVhZGVyPVQsIHNlcD0iLCIpCgojUmVuYW1lIGNvbHVtbnMKY29scyA8LSBjKCJlbnNlbWJsX2dlbmVfaWQiLCAiU0MwMV9kYXkwX3JlcDEiLCAiU0MwMV9kYXkwX3JlcDIiLCAiU0MwMV9kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMDFfZGF5M19yZXAxIiwgIlNDMDFfZGF5M19yZXAyIiwgIlNDMDFfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzAxX2RheThfcmVwMSIsICJTQzAxX2RheThfcmVwMiIsICJTQzAxX2RheThfcmVwMyIsCiAgICAgICAgICAiU0MwN19kYXkwX3JlcDEiLCAiU0MwN19kYXkwX3JlcDIiLCAiU0MwN19kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMDdfZGF5M19yZXAxIiwgIlNDMDdfZGF5M19yZXAyIiwgIlNDMDdfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzA3X2RheThfcmVwMSIsICJTQzA3X2RheThfcmVwMiIsICJTQzA3X2RheThfcmVwMyIsCiAgICAgICAgICAiU0MxMF9kYXkwX3JlcDEiLCAiU0MxMF9kYXkwX3JlcDIiLCAiU0MxMF9kYXkwX3JlcDMiLAogICAgICAgICAgIlNDMTBfZGF5M19yZXAxIiwgIlNDMTBfZGF5M19yZXAyIiwgIlNDMTBfZGF5M19yZXAzIiwKICAgICAgICAgICJTQzEwX2RheThfcmVwMSIsICJTQzEwX2RheThfcmVwMiIsICJTQzEwX2RheThfcmVwMyIpCm5hbWVzKGQpIDwtIGNvbHMKZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKZ2VuZXMgPC0gZCRlbnNlbWJsX2dlbmVfaWQKR19saXN0IDwtIGdldEJNKGF0dHJpYnV0ZXM9IGMoImVuc2VtYmxfZ2VuZV9pZCIsImhnbmNfc3ltYm9sIiksCiAgICAgICAgICAgICAgICBmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwKICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKICAgICAgICAgICAgICAgIG1hcnQ9bWFydCkKCkdFX2RhdGEgPC0gbWVyZ2UoZCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQpkIDwtIEdFX2RhdGFbLCAtMV0KZCA8LSBkW2MoMjgsIHNlcSgxOjI3KSldCnJvd25hbWVzKGQpIDwtIG1ha2UubmFtZXMoZCRoZ25jX3N5bWJvbCwgdW5pcXVlID0gVCkKZCA8LSBkWywgMjoyOF0KCiMgcmVtb3ZlIGdlbmVzIHdpdGggPDUgY291bnRzIGluIGFsbCBzYW1wbGVzCmQgPC0gZFthcHBseShkLCAxLCBmdW5jdGlvbih4KSBhbGwoeCA+IDUpKSxdCgoKY291bnRkYXRhIDwtIGQKCmhlYWQoY291bnRkYXRhKQpucm93KGNvdW50ZGF0YSkKbmNvbChjb3VudGRhdGEpCmBgYAoKIyMjIElkZW50aWZ5aW5nIGRpZmZlcmVudCBpb24gY2hhbm5lbCBnZW5lIGxpc3RzCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmNvdW50ZGF0YV9oaXN0YW1pbmUgPSBjb3VudGRhdGFbYygiSFJIMSIsIkhSSDIiLCJIUkgzIiwiSFJINCIpLF0KY291bnRkYXRhX0lQcyA9IGNvdW50ZGF0YVtjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpLF0KY291bnRkYXRhX0lQcmVsID0gY291bnRkYXRhW2MoIklUUFIxIiwgIklUUFIyIiwgIklUUFIzIiwgIlBMQ0cxIiwiREdLQSIsICJER0tCIiwgIkRHS0QiLCAiREdLRSIsICJER0tHIiwgIkRHS0giLCAiREdLSSIsICJER0tLIiwgIkRHS1EiLCAiREdLWiIsICJQSUszQ0EiLCAiUElLM0NCIiwgIlBJSzNDRCIsICJQSUszQ0ciLCAiUElLM0MyQSIsICJQSUszQzJCIiwgIlBJSzNDMkciLCAiUElLM0MzIiksXQpjb3VudGRhdGFfbXVzYyA9IGNvdW50ZGF0YVtjKCJDSFJNMSIsICJDSFJNMiIsICJDSFJNMyIsICJDSFJNNCIsICJDSFJNNSIpLF0KY291bnRkYXRhX2dsdXQgPSBjb3VudGRhdGFbYygiR1JNMSIsICJHUk0yIiwgIkdSTTMiLCAiR1JNNCIsICJHUk01IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JNNiIsICJHUk03IiwgIkdSTTgiLCAiR1JJQTEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR1JJQTIiLCAiR1JJQTMiLCAiR1JJQTQiLCAiR1JJRDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklEMiIsICJHUklLMSIsICJHUklLMiIsICJHUklLMyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklLNCIsICJHUklLNSIsICJHUklOMSIsICJHUklOMkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOMkIiLCAiR1JJTjJDIiwgIkdSSU4yRCIsICJHUklOM0EiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHUklOM0IiKSxdCgojIyMgUGxvdCBJUCMgZGF0YQojIHNvdXJjZSgic3VtbWFyeVNFLlIiKQpJUHNfbWF0Y2ggPSBjb2xzcGxpdChjb2xuYW1lcyhjb3VudGRhdGFfSVBzKSwgcGF0dGVybiA9ICJfIiwgbmFtZXMgPSBjKCJQb3B1bGF0aW9uIiwgIlRpbWUiLCAiUmVwbGljYXRlIikpCklQc19wbG90ID0gY2JpbmQoSVBzX21hdGNoLCB0KGNvdW50ZGF0YV9JUHMpKQpJUHNfbWVsdCA9IG1lbHQoZGF0YSA9IElQc19wbG90LCBpZC52YXJzID0gYygiUG9wdWxhdGlvbiIsICJUaW1lIiwgIlJlcGxpY2F0ZSIpLCBtZWFzdXJlLnZhcnMgPSBjKCJJVFBSMSIsICJJVFBSMiIsICJJVFBSMyIpKQpJUHNfZGF0ID0gUk1pc2M6OnN1bW1hcnlTRShJUHNfbWVsdCwgbWVhc3VyZXZhciA9ICJ2YWx1ZSIsIGdyb3VwdmFycyA9IGMoIlBvcHVsYXRpb24iLCAiVGltZSIsICJ2YXJpYWJsZSIpKQpJUHNfZGF0JFRpbWUgPSBhcy5udW1lcmljKGdzdWIoIlteWzpkaWdpdDpdXSIsIiIsSVBzX2RhdCRUaW1lKSkKSVBzX2RhdF9zdWIgPSBzdWJzZXQoSVBzX2RhdCwgdmFyaWFibGUgJWluJSBjKCJJVFBSMiIsIklUUFIzIikpCklQc19nZ3Bsb3RlZCA8LSBnZ3Bsb3QoSVBzX2RhdF9zdWIsIGFlcyh4PVRpbWUsIHk9dmFsdWUsIGdyb3VwID0gaW50ZXJhY3Rpb24odmFyaWFibGUsIFBvcHVsYXRpb24pKSkgKyBnZW9tX2xpbmUoc2l6ZT0xLjUsIGFlcyhjb2xvciA9IFBvcHVsYXRpb24sIGxpbmV0eXBlID0gdmFyaWFibGUpKSArIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgYWVzKGNvbG9yID0gUG9wdWxhdGlvbikpICsKZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj12YWx1ZS1zZCwgeW1heD12YWx1ZStzZCwgY29sb3IgPSBQb3B1bGF0aW9uKSwgd2lkdGg9LjIsIHNpemU9MS41KSArCnRoZW1lX2J3KCkgKyB4bGFiKCJUaW1lIChkYXlzKSIpICsgeWxhYigiR2VuZSBDb3VudHMiKSArCmdndGl0bGUoIklQMyBnZW5lIHJlY2VwdG9ycyIpICsKdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwgCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIsZmFjZT0iYm9sZCIpLAogICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSkKCklQc19nZ3Bsb3RlZAojIGdnc2F2ZSgiSVAzUjJfM19UaW1lU2VyaWVzUk5Bc2VxX2Nsb25lcy5wZGYiLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKIyMjMi4gQ29udmVydCBjb3VudHMgdGFibGUgdG8gREVTZXEyIG9iamVjdC4gCkNvbnZlcnQgY291bnRzIHRhYmxlIHRvIG9iamVjdCBmb3IgREVTZXEyIG9yIGFueSBvdGhlciBhbmFseXNpcyBwaXBlbGluZS4gVGhpcyBzdGVwIHdpbGwgcmVxdWlyZSB0byBwcmVwYXJlIGRhdGEgb2JqZWN0IGluIGEgZm9ybSB0aGF0IGlzIHN1aXRhYmxlIGZvciBhbmFseXNpcyBpbiBERVNlcTIgcGlwZWxpbmU6IHdlIHdpbGwgbmVlZCB0aGUgZm9sbG93aW5nIHRvIHByb2NlZWQ6CiAgCiAgKyBjb3VudGRhdGE6IGEgdGFibGUgd2l0aCB0aGUgcmVhZC9mcmFnbWVudCBjb3VudHMuIAorIGNvbGRhdGE6IGEgdGFibGUgd2l0aCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc2FtcGxlcy4gCgpVc2luZyB0aGUgbWF0cml4IG9mIGNvdW50cyBhbmQgdGhlIHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZSwgd2UgbmVlZCB0byBjb25zdHJ1Y3QgdGhlIERFU2VxRGF0YVNldCBvYmplY3QsIGZvciB3aGljaCB3ZSB3aWxsIHVzZSBERVNlcURhdGFTZXRGcm9tTWF0cml4Li4uLi4KCiMjIyMgMS4gRGVmaW5lIHRoZSBzYW1wbGVzIGFuZCB0cmVhdG1lbnQgY29uZGl0aW9ucy4gCmBgYHtyfQpjb25kaXRpb24gPC0gYygiMCIsICIzIiwgIjgiKQp0cmVhdG1lbnQgPC0gcmVwKGNvbmRpdGlvbiwgZWFjaD0zKSAjIFRocmVlIGJpb2xvZ2ljYWwgcmVwbGljYXRlcwp1bmlxdWUodHJlYXRtZW50KQpjZWxsIDwtIGMoIlNDMDEiLCAiU0MwNyIsIlNDMTAiKSAjc3VibGluZXMgdXNlZCBmb3IgdGhlIGFuYWx5c2lzCmNlbGxOYW1lIDwtIHJlcChjZWxsLCBlYWNoPTMpCgpjb2xkYXRhIDwtIGRhdGEuZnJhbWUoY2VsbD1yZXAoY2VsbE5hbWUpLCB0cmVhdG1lbnQ9cmVwKHRyZWF0bWVudCwgZWFjaD0zKSkKZ3JvdXAgPSBmYWN0b3IocGFzdGUoY29sZGF0YSRjZWxsLCBjb2xkYXRhJHRyZWF0bWVudCwgc2VwPSIuIikpCmNvbGRhdGEkZ3JvdXAgPSBncm91cApgYGAKCiMjIyMgMi4gY29uc3RydWN0IHRoZSBERVNlcURhdGFTZXQgb2JqZWN0IGZyb20gdGhlIG1hdHJpeCBvZiBjb3VudHMgYW5kIHRoZSBzYW1wbGUgaW5mb3JtYXRpb24gdGFibGUuIApEZXNjcmliZWQgYWJvdmUgYXJlOiBjb3VudGRhdGEtIHJhdyBjb3VudHMsIGNvbGRhdGE6IHNhbXBsZSBpbmZvcm1hdGlvbiB0YWJsZS4gCmBgYHtyfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBjb2xkYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGNlbGwgKyB0cmVhdG1lbnQgKyBjZWxsOnRyZWF0bWVudCkKZGRzCm5yb3coZGRzKTsgbmNvbChkZHMpCmBgYAoKIyMjMy4gRXhwbG9yYXRvcnkgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uClRoZXJlIGFyZSB0d28gc2VwYXJhdGUgc3RlcHMgaW4gdGhlIHdvcmtmbG93OyB0aGUgb25lIHdoaWNoIGludm9sdmVzIGRhdGEgdHJhbnNmb3JtYXRpb25zIGluIG9yZGVyIHRvIHZpc3VhbGl6ZSBzYW1wbGUgcmVsYXRpb25zaGlwcyBhbmQgdGhlIHNlY29uZCBzdGVwIGludm9sdmVzIHN0YXRpc3RpY2FsIHRlc3RpbmcgbWV0aG9kcyB3aGljaCByZXF1aXJlcyB0aGUgb3JpZ2luYWwgcmF3IGNvdW50cy4gCgojIyMjIDEuIFByZS1maWx0ZXJpbmcgYW5kIG5vcm1hbGl6YXRpb24uIApQcmUtZmlsdGVyaW5nIGFuZCBub3JtYWxpemF0aW9uIGlzIHJlcXVpcmVkIHRvIHJlbW92ZSBsb3dseSBleHByZXNzZWQgZ2VuZXMuIAoKYGBge3J9CmRkczIgPC0gZGRzW3Jvd1N1bXMoY291bnRzKGRkcykpID4gMTgsIF0gIyByZW1vdmUgcm93cyB3aXRoIG1pbmltdW0gb2YgMiByZWFkIHBlciBjb25kaXRpb24KCm5yb3coZGRzMikKIyBzYXZlKGRkczIsIGZpbGUgPSAiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCiMgbG9hZCgiRERTX1NDLTEsNywxMF9jZWxsLXRyZWF0LWludC5SRGF0YSIpCgpgYGAKCiMjIyMgMi4gVmlzdWFsaXplIHNhbXBsZS10by1zYW1wbGUgZGlzdGFuY2VzLiAKV2UgY291bGQgdXNlIFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMgKFBDQSkgdG8gdmlzdWFsaXplIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBzYW1wbGVzLiAKYGBge3J9CnJsZCA8LSBybG9nKGRkczIsIGJsaW5kID0gRkFMU0UpCiMgc2F2ZShybGQsIGZpbGUgPSAiUkxEX1NDLTEsNywxMF8wLDMsOGRfMjAxODA3MDEuUkRhdGEiKQojIGxvYWQoIlJMRF9TQy0xLDcsMTBfMCwzLDhkXzIwMTgwNzAxLlJEYXRhIikKcGxvdFBDQShybGQsIGludGdyb3VwID0gYygiY2VsbCIsICJ0cmVhdG1lbnQiKSwgbnRvcD01MDAwKQoKIyMgVXNlIHByY29tcCBmdW5jdGlvbgojIENvbG9yZWQgYnkgY2VsbCBsaW5lLCBzaGFwZSBieSB0aW1lIHBvaW50LCBsaW5lcyBjb25uZWN0aW5nIHRpbWUKcGNhX0RFc2VxIDwtIHByY29tcCh0KGFzc2F5KHJsZCkpKQpwY2FfREVzZXFfcGVyYyA8LSByb3VuZCgxMDAqcGNhX0RFc2VxJHNkZXZeMi9zdW0ocGNhX0RFc2VxJHNkZXZeMiksMSkKcGNhX0RFc2VxX2RmIDwtIGRhdGEuZnJhbWUoUEMxID0gcGNhX0RFc2VxJHhbLDFdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgUEMyID0gcGNhX0RFc2VxJHhbLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlID0gY29sbmFtZXMoYXNzYXkocmxkKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNlbGwubGluZSA9IHJlcChjKCJTQzAxIiwgIlNDMDciLCAiU0MxMCIpLCBlYWNoID0gOSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRheSA9IHJlcChjKCJEYXkwIiwgIkRheTMiLCAiRGF5OCIpLCBlYWNoID0gMyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxpY2F0ZSA9IHJlcChjKCJSZXAxIiwgIlJlcDIiLCAiUmVwMyIpLCB0aW1lcz05KSkKCnBjYV9ERXNlcV9tZWFucyA8LSBkZHBseShwY2FfREVzZXFfZGYsIC4oY2VsbC5saW5lLCBkYXkpLCBzdW1tYXJpc2UsIG1lYW5QQzEgPSBtZWFuKFBDMSksIG1lYW5QQzIgPSBtZWFuKFBDMikpCgpnZ3Bsb3QocGNhX0RFc2VxX2RmLCBhZXMoUEMxLFBDMiwgY29sb3IgPSBjZWxsLmxpbmUpKSsKICBnZW9tX3BvaW50KGFlcyhzaGFwZSA9IGRheSksIHNpemU9NSkgKwogIGdlb21fcGF0aChkYXRhID0gcGNhX0RFc2VxX21lYW5zLCAKICAgICAgICAgICAgYWVzKHg9bWVhblBDMSwgeT1tZWFuUEMyLAogICAgICAgICAgICAgICAgY29sb3I9Y2VsbC5saW5lKSwgYXJyb3cgPSBhcnJvdygpLAogICAgICAgICAgICBzaXplID0gMikgKwogIGxhYnMoeD1wYXN0ZTAoIlBDMSAoIixwY2FfREVzZXFfcGVyY1sxXSwiJSB2YXJpYW5jZSkiKSwgeT1wYXN0ZTAoIlBDMiAoIixwY2FfREVzZXFfcGVyY1syXSwiJSB2YXJpYW5jZSkiKSkgKwogIHRoZW1lX2J3KCkgKyBnZ3RpdGxlKCJQQ0EgLSBTdWJjbG9uZXMgaW4gVGltZSIpICsKICB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLCAKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQogIAoKCmBgYAoKIyMjIDQuIERpZmZlcmVudGlhbCBFeHByZXNzaW9uIEFuYWx5c2lzLiAKQWx3YXlzIG1ha2Ugc3VyZSB0byB1c2UgdGhlIHVubm9ybWFsaXplZCByYXcgY291bnRzIGZvciB0aGlzLiBXZSB3aWxsIHVzZSBERVNlcSBmdW5jdGlvbiB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBhbmFseXNpcyBiZXR3ZWVuIHNhbXBsZXM7IFVubGVzcyBzcGVjaWZpZWQsIHRoZSBhbmFseXNpcyBpcyBiZXR3ZWVuIHRoZSBsYXN0IGdyb3VwIGFuZCB0aGUgZmlyc3QgZ3JvdXAuIERpZmZlcmVudCBjb21wYXJpc29uIGNhbiBiZSBkb25lIHVzaW5nICdjb250cmFzdCcgYXJndW1lbnQuIFN0ZXBzIGludm9sdmVkIHVuZGVybmVhdGg6CiAgCjEuIGVzdGltYXRpb24gb2Ygc2l6ZSBmYWN0b3JzIChjb250cm9scyBmb3IgZGlmZmVyZW5jZXMgaW4gc2VxdWVuY2luZyBkZXB0aCBvZiB0aGUgc2FtcGxlcykKMi4gZXN0aW1hdGlvbiBvZiBkaXNwZXJzaW9uIHZhbHVlcyBmb3IgZWFjaCBnZW5lLAozLiBmaXR0aW5nIGEgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsCgojIyMjIDEuIFJ1bm5pbmcgdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHBpcGVsaW5lLiAKYGBge3IsIGNhY2hlPVRSVUV9CmRlc2lnbihkZHMyKSA9IH4gY2VsbCArIHRyZWF0bWVudCArIGNlbGw6dHJlYXRtZW50CmRkcyA8LSBERVNlcShkZHMyLCB0ZXN0ID0gIkxSVCIsIHJlZHVjZWQgPSB+IGNlbGwgKyB0cmVhdG1lbnQpCiMgc2F2ZShkZHMsIGZpbGUgPSAiREVTZXFfU0MxLDcsMTBfVGltZWNvdXJzZV9MUlQuUkRhdGEiKQojIGxvYWQoIkRFU2VxX1NDMSw3LDEwX1RpbWVjb3Vyc2VfTFJULlJEYXRhIikKIyBkZHMKYGBgCgojIyMjIDIuIEJ1aWxkaW5nIHRoZSByZXN1bHRzIHRhYmxlLiAKQnkgZGVmYXVsdCwgcmVzdWx0cyB3aWxsIGV4dHJhY3QgdGhlIGVzdGltYXRlZCBsb2cyIGZvbGQgY2hhbmdlcyBhbmQgcCB2YWx1ZXMgZm9yIHRoZSBsYXN0IHZhcmlhYmxlIGluIHRoZSBkZXNpZ24gZm9ybXVsYS4gSWYgdGhlcmUgYXJlIG1vcmUgdGhhbiAyIGxldmVscyBmb3IgdGhpcyB2YXJpYWJsZSwgcmVzdWx0cyB3aWxsIGV4dHJhY3QgdGhlIHJlc3VsdHMgdGFibGUgZm9yIGEgY29tcGFyaXNvbiBvZiB0aGUgbGFzdCBsZXZlbCBvdmVyIHRoZSBmaXJzdCBsZXZlbC4gCmBgYHtyfQojIEVzaW1hdGUgdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gZ3JvdXBzIGJ5OiAjIGEpIExvd2VyaW5nIHRoZSBGRFIgKHBhZGopIG9yIChiKSByYWlzZSB0aGUgbG9nMiBmb2xkIGNoYW5nZS4KCnJlc3VsdHNOYW1lcyhkZHMpCiMgYWxwaGEgPSBGRFIgYWRqdXN0ZWQgcCB2YWx1ZSBjdXRvZmYKcmVzIDwtIHJlc3VsdHMoZGRzLCBhbHBoYSA9IDAuMDAxKQpzdW1tYXJ5KHJlcykKcmVzT3JkZXJlZCA8LSByZXNbb3JkZXIocmVzJHB2YWx1ZSksXQpyZGF0YSA9IGFzLmRhdGEuZnJhbWUocmVzKQpgYGAKIyMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uOiBkYXlzIDAgdG8gOApDaGFuZ2Ugc2lnbmlmaWNhbnQgbG9nMiBmb2xkIGNoYW5nZSB0byAxLjU4NSAoPT0gMy1mb2xkIGNoYW5nZSBpbiBsb2cyIHNwYWNlKS4KYGBge3J9CnJlc18wdG84ZCA8LSByZXN1bHRzKGRkcywgbmFtZT0idHJlYXRtZW50XzhfdnNfMCIsIGNvb2tzQ3V0b2ZmID0gMC45OSwgCiAgICAgICAgICAgICAgICAgICAgIGluZGVwZW5kZW50RmlsdGVyaW5nID0gVFJVRSwgYWxwaGEgPSAwLjA1LCBwQWRqdXN0TWV0aG9kID0gIkJIIikKc3VtbWFyeShyZXNfMHRvOGQpCiMgb3JkZXIgcmVzdWx0cyB0YWJsZSBieSB0aGUgc21hbGxlc3QgYWRqdXN0ZWQgcCB2YWx1ZToKcmVzXzB0bzhkIDwtIHJlc18wdG84ZFtvcmRlcihyZXNfMHRvOGQkcGFkaiksXQpyZXN1bHRzXzB0bzhkIDwtIGFzLmRhdGEuZnJhbWUocmVzXzB0bzhkKQoKcmVzdWx0c18wdG84ZCA8LSBtdXRhdGUocmVzdWx0c18wdG84ZCwgc2lnPWlmZWxzZShyZXN1bHRzXzB0bzhkJHBhZGo8MC4wNSAmIHJlc3VsdHNfMHRvOGQkbG9nMkZvbGRDaGFuZ2UgPiAxLjU4NSwgIlVwcmVndWxhdGVkIiwgaWZlbHNlKHJlc3VsdHNfMHRvOGQkcGFkajwwLjA1ICYgcmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSA8IC0xLjU4NSwgIkRvd25yZWd1bGF0ZWQiLCAiTm90IFNpZ25pZmljYW50IikpKQoKcm93Lm5hbWVzKHJlc3VsdHNfMHRvOGQpIDwtIHJvdy5uYW1lcyhyZXNfMHRvOGQpCgoKaGVhZChyZXN1bHRzXzB0bzhkKQpERWdlbmVzXzB0bzhkIDwtIHJlc3VsdHNfMHRvOGRbd2hpY2goYWJzKHJlc3VsdHNfMHRvOGQkbG9nMkZvbGRDaGFuZ2UpID4gbG9nMigxLjUpICYgcmVzdWx0c18wdG84ZCRwYWRqIDwgMC4wNSksXQoKaWYoU0FWRUZJTEVTKSB3cml0ZS5jc3YoREVnZW5lc18wdG84ZCwgZmlsZT0ifi9EZXNrdG9wL0RFZ2VuZXNfMHRvOGQuY3N2IikKYGBgCgojIyMgVm9sY2FubyBwbG90CmBgYHtyfQp2b2xjYW5vIDwtIGdncGxvdChyZXN1bHRzXzB0bzhkLCBhZXMobG9nMkZvbGRDaGFuZ2UsIC1sb2cxMChwdmFsdWUpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbCA9IHNpZykpICsgdGhlbWVfYncoKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoInJlZCIsICJncmV5IiwgImdyZWVuMyIpKSArCiAgIyBnZ3RpdGxlKCJWb2xjYW5vIFBsb3Qgb2YgVW50cmVhdGVkIHZzIElkbGluZyIpICsKICBsYWJzKHg9ImxvZzIoRm9sZCBDaGFuZ2UpIiwgeT0iTG9nKE9kZHMgUmF0aW8pIikgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBoanVzdCA9IDAuNSwgZmFjZSA9ICJib2xkIiksIAogICAgICAgIGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLCAKICAgICAgICBheGlzLnRpdGxlPWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCgp2b2xjYW5vCiMgdm9sY2FubyArIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChkYXRhPXJlc3VsdHNfMHRvOGRbMToxMCwgXSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3Bsb3QyOjphZXMobGFiZWw9cm93bmFtZXMocmVzdWx0c18wdG84ZFsxOjEwLCBdKSkpCiMgc2F2ZShyZXN1bHRzXzB0bzhkLCBmaWxlPSJ1bnRyZWF0ZWRJZGxpbmdfREVBLlJEYXRhIikKYGBgCgoKYGBge3J9CkRFZ2VuZXNfMHRvOGQgPC0gREVnZW5lc18wdG84ZFtvcmRlcihhYnMoREVnZW5lc18wdG84ZCRsb2cyRm9sZENoYW5nZSksREVnZW5lc18wdG84ZCRzaWcsIGRlY3JlYXNpbmcgPSBUUlVFKSxdCnRlbXAgPC0gREVnZW5lc18wdG84ZFtERWdlbmVzXzB0bzhkJGJhc2VNZWFuID4gMzAwLF0KdGVtcCA8LSB0ZW1wW2Ficyh0ZW1wJGxvZzJGb2xkQ2hhbmdlKT4yLF0KaWYoU0FWRUZJTEVTKSB3cml0ZS5jc3YoREVnZW5lc18wdG84ZCwgZmlsZSA9ICJERWdlbmVzXzB0bzhkLmNzdiIpCmBgYAoKCiMgR2VuZXJhdGluZyBJb24gQ2hhbm5lbCBTcGVjaWZpYyBHZW5lIERhdGFmcmFtZXMKYGBge3J9CnRlc3QgPC0gYXNzYXkoZGRzKQp0eXBlcyA8LSBjKCJBVFAiLCAiVFJQIiwgIkdBQlIiLCAiQ1JBQ1IiLCAiU0xDIiwgIktDTiIsICJDQUNOIiwgIkdSSSIsICJBQkMiLCAiU0NOIiwgIlRSUCIsICJSSUMzIiwgIkNIUk5EIiwgIlJZUiIpCnNhbXBsZXMgPC0gYygiU0MwMV9kYXkwIiwgIlNDMDFfZGF5MyIsICJTQzAxX2RheTgiLCAiU0MwN19kYXkwIiwgIlNDMDdfZGF5MyIsICJTQzA3X2RheTgiLCAiU0MxMF9kYXkwIiwgIlNDMTBfZGF5MyIsICJTQzEwX2RheTgiKQp0ZXN0IDwtIHRlc3RbZ3JlcChwYXN0ZSh0eXBlcywgY29sbGFwc2U9InwiKSwgcm93bmFtZXModGVzdCkpLF0KdGVzdDEgPC0gc2FwcGx5KHNhbXBsZXMsIGZ1bmN0aW9uKHgpIHJvd01lYW5zKHRlc3RbLCBncmVwKHgsIGNvbG5hbWVzKHRlc3QpKV0pKQpyb3duYW1lcyh0ZXN0MSkgPC0gcm93bmFtZXModGVzdCkKdGVzdDEgPC0gdGVzdDFbb3JkZXIocm93bmFtZXModGVzdDEpKSxdCnRlc3QyIDwtIGFzLmRhdGEuZnJhbWUodGVzdDEpCnRlc3QyWyJsMkZDX1NDMDFfMHRvOCJdIDwtIGxvZzIodGVzdDJbIlNDMDFfZGF5OCJdL3Rlc3QyWyJTQzAxX2RheTAiXSkKdGVzdDJbImwyRkNfU0MwN18wdG84Il0gPC0gbG9nMih0ZXN0MlsiU0MwN19kYXk4Il0vdGVzdDJbIlNDMDdfZGF5MCJdKQp0ZXN0MlsibDJGQ19TQzEwXzB0bzgiXSA8LSBsb2cyKHRlc3QyWyJTQzEwX2RheTgiXS90ZXN0MlsiU0MxMF9kYXkwIl0pCnRlc3QzIDwtIHN1YnNldCh0ZXN0MiwgbDJGQ19TQzAxXzB0bzggPiAxICYgbDJGQ19TQzA3XzB0bzggPiAxICYgbDJGQ19TQzEwXzB0bzggPiAxKQp0ZXN0NCA8LSBzdWJzZXQodGVzdDIsIGwyRkNfU0MwMV8wdG84ID4gMSB8IGwyRkNfU0MwN18wdG84ID4gMSB8IGwyRkNfU0MxMF8wdG84ID4gMSkKIyB3cml0ZS5jc3YoeCA9IHRlc3QyLCBmaWxlID0gImFsbF9pb25DaGFubmVsX0V4cHJlc3Npb24uY3N2IikKIyB3cml0ZS5jc3YoeCA9IHRlc3QzLCBmaWxlID0gImFsbFVwcmVnX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQojIHdyaXRlLmNzdih4ID0gdGVzdDQsIGZpbGUgPSAiYXRMZWFzdE9uZVVwcmVnX2lvbkNoYW5uZWxfRXhwcmVzc2lvbi5jc3YiKQp0ZXN0NSA8LSBsb2cyKHRlc3Q0WywgMTo5XSsxKQpwaGVhdG1hcCh0ZXN0NSwgY2x1c3Rlcl9jb2xzID0gRiwgY2x1c3Rlcl9yb3dzID0gRikKdGVzdDYgPC0gbG9nMigodGVzdDNbLDE6OV0pKzEpCnBoZWF0bWFwKHRlc3Q2LCBjbHVzdGVyX3Jvd3MgPSBGLCBjbHVzdGVyX2NvbHMgPSBGKQp0ZXN0NyA8LSB0ZXN0NVtyb3dTdW1zKHRlc3Q1KT4zMCxdCnBoZWF0bWFwKHRlc3Q3LCBjbHVzdGVyX3Jvd3MgPSBGLCBjbHVzdGVyX2NvbHMgPSBGKQpgYGAKCmBgYHtyfQojIGxvYWQoZmlsZT0idW50cmVhdGVkSWRsaW5nX0RFQS5SRGF0YSIpCgpPcmdEQiA8LSBvcmcuSHMuZWcuZGIKdXByZWdfZ2VuZXMgPC0gc3Vic2V0KHJlc3VsdHNfMHRvOGQsIHBhZGo8MC4wNSAmIGxvZzJGb2xkQ2hhbmdlPjIpCmRvd25yZWdfZ2VuZXMgPC1zdWJzZXQocmVzdWx0c18wdG84ZCwgcGFkajwwLjA1ICYgbG9nMkZvbGRDaGFuZ2U8KC0yKSkKCmdlbmVMaXN0X3VwIDwtIGFzLnZlY3Rvcih1cHJlZ19nZW5lcyRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfdXApIDwtIHJvd25hbWVzKHVwcmVnX2dlbmVzKQpnZW5lTGlzdF9kb3duIDwtIGFzLnZlY3Rvcihkb3ducmVnX2dlbmVzJGxvZzJGb2xkQ2hhbmdlKQpuYW1lcyhnZW5lTGlzdF9kb3duKSA8LSByb3duYW1lcyhkb3ducmVnX2dlbmVzKQoKZ2VuZXNfdXAgPC0gYXMudmVjdG9yKHJvd25hbWVzKHVwcmVnX2dlbmVzKSkKZ2VuZXNfZG93biA8LSBhcy52ZWN0b3Iocm93bmFtZXMoZG93bnJlZ19nZW5lcykpCiMgbmFtZXMoZ2VuZUxpc3QpIDwtIHJvd25hbWVzKHJlc3VsdHNfMHRvOGQpCmdlbmVzX3VwX0VOVFJFWklEIDwtIGJpdHIoZ2VuZXNfdXAsIGZyb21UeXBlID0gIlNZTUJPTCIsIHRvVHlwZSA9ICJFTlRSRVpJRCIsIE9yZ0RiID0gT3JnREIpJEVOVFJFWklECmdlbmVzX2Rvd25fRU5UUkVaSUQgPC0gYml0cihnZW5lc19kb3duLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRAoKIyBHcm91cCBHTwpnZ29fdXAgPC0gY2x1c3RlclByb2ZpbGVyOjpncm91cEdPKGdlbmUgICAgID0gZ2VuZXNfdXBfRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgICAgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlID0gVFJVRSkKZ2dvX3VwX2RmIDwtIGFzLmRhdGEuZnJhbWUoZ2dvX3VwKQpnZ29fdXBfZGYgPC0gZ2dvX3VwX2RmW29yZGVyKC1nZ29fdXBfZGYkQ291bnQpLF0gCgpnZ29fZG93biA8LSBjbHVzdGVyUHJvZmlsZXI6Omdyb3VwR08oZ2VuZSA9IGdlbmVzX2Rvd25fRU5UUkVaSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3JnRGIgICAgPSBPcmdEQiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbnQgICAgICA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWwgICAgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlYWRhYmxlID0gVFJVRSkKIyBWaWV3KGFzLmRhdGEuZnJhbWUoZ2dvX2Rvd24pKQoKIyBHTyBvdmVyLXJlcHJlc2VudGF0aW9uIHRlc3QKZWdvX2dlbmVzVXAgPC0gY2x1c3RlclByb2ZpbGVyOjplbnJpY2hHTyhnZW5lICA9IGdlbmVzX3VwX0VOVFJFWklELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgID0gT3JnREIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmICA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgICAgICA9IFRSVUUpCgojIFZpZXcoYXMuZGF0YS5mcmFtZShlZ29fZ2VuZXNVcCkpCgplZ29fZ2VuZXNEb3duIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoR08oZ2VuZSAgPSBnZW5lc19kb3duX0VOVFJFWklELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPcmdEYiAgICAgICAgID0gT3JnREIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9udCAgICAgICAgICAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwQWRqdXN0TWV0aG9kID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHZhbHVlQ3V0b2ZmICA9IDAuMDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHF2YWx1ZUN1dG9mZiAgPSAwLjA1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZGFibGUgICAgICA9IFRSVUUpCgojIFZpZXcoYXMuZGF0YS5mcmFtZShlZ29fZ2VuZXNEb3duKSkKCiMga2tfZ2VuZXNVcCA8LSBlbnJpY2hLRUdHKGdlbmUgPSBnZW5lc191cF9FTlRSRVpJRCwKIyAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAnaHNhJywKIyAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQojIFZpZXcoYXMuZGF0YS5mcmFtZShra19nZW5lc1VwKSkKIyAKIyBra19nZW5lc0Rvd24gPC0gZW5yaWNoS0VHRyhnZW5lID0gZ2VuZXNfZG93bl9FTlRSRVpJRCwKIyAgICAgICAgICAgICAgICAgICAgb3JnYW5pc20gPSAnaHNhJywKIyAgICAgICAgICAgICAgICAgICBwdmFsdWVDdXRvZmYgPSAwLjA1KQojIFZpZXcoYXMuZGF0YS5mcmFtZShra19nZW5lc0Rvd24pKQoKIyBlZ29fR1NFQV91cCA8LSBnc2VHTyhnZW5lTGlzdCA9IGdlbmVMaXN0X3VwLAojICAgICAgICAgICAgICAgT3JnRGIgICAgICAgID0gT3JnREIsCiMgICAgICAgICAgICAgICBvbnQgICAgICAgICAgPSAiQlAiLAojICAgICAgICAgICAgICAgblBlcm0gICAgICAgID0gMTAwMCwKIyAgICAgICAgICAgICAgIG1pbkdTU2l6ZSAgICA9IDEwMCwKIyAgICAgICAgICAgICAgIG1heEdTU2l6ZSAgICA9IDUwMCwKIyAgICAgICAgICAgICAgIHB2YWx1ZUN1dG9mZiA9IDAuMDUsCiMgICAgICAgICAgICAgICB2ZXJib3NlICAgICAgPSBGQUxTRSkKCiMgYmFycGxvdChnZ29fdXAsIG9yZGVyPVQpCiMgYmFycGxvdChnZ29fZG93bikKZG90cGxvdChlZ29fZ2VuZXNVcCkgKyBnZ3RpdGxlKCJHTyBPdmVyLXJlcHJlc2VudGF0aW9uIFVwcmVndWxhdGVkIEdlbmVzIikgKwogIGxhYnMoeD0iR2VuZSBSYXRpbyIsIHk9IkdPIFRlcm1zIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMixmYWNlPSJib2xkIiksIAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIsIGZhY2U9ImJvbGQiKSkKCmRvdHBsb3QoZWdvX2dlbmVzRG93bikgKyBnZ3RpdGxlKCJHTyBPdmVyLXJlcHJlc2VudGF0aW9uIERvd25yZWd1bGF0ZWQgR2VuZXMiKSArCiAgbGFicyh4PSJHZW5lIFJhdGlvIiwgeT0iR08gVGVybXMiKSArCiAgdGhlbWUobGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpKQoKIyBlbWFwcGxvdChlZ29fZ2VuZXNVcCkKIyBlbWFwcGxvdChlZ29fZ2VuZXNEb3duKQpjbmV0cGxvdChlZ29fZ2VuZXNVcCwgY2F0ZWdvcnlTaXplPSJwdmFsdWUiLCBmb2xkQ2hhbmdlPWdlbmVMaXN0X3VwKQpjbmV0cGxvdChlZ29fZ2VuZXNEb3duLCBjYXRlZ29yeVNpemU9InB2YWx1ZSIsIGZvbGRDaGFuZ2U9Z2VuZUxpc3RfZG93bikKCgplZ29fZ2VuZXNVcF9kZiA8LSBhcy5kYXRhLmZyYW1lKGVnb19nZW5lc1VwKSAKZWdvVXAgPC0gZWdvX2dlbmVzVXBfZGZbb3JkZXIoLWVnb19nZW5lc1VwX2RmJENvdW50KSxdCiMgc29ydGVkX2Vnb1VwX3RvcDEwIDwtIGhlYWQoZWdvVXAsIDEwKQplZ29VcF9nZW5lcyA8LSBzdHJzcGxpdChlZ29VcCRnZW5lSUQsICIvIiwgZml4ZWQ9VFJVRSkKIyBlZ29VcF90b3AxMF9nZW5lc19hbGwgPC0gdW5saXN0KHN0cnNwbGl0KGhlYWQoZWdvVXAsIDEwKSRnZW5lSUQsICJbL10iKSkKIyBlZ29VcF90b3AxMF9nZW5lc19ncm91cCA8LSBzdHJzcGxpdChzb3J0ZWRfZWdvVXBfdG9wMTAkZ2VuZUlELCAiWy9dIikKIyBlZ29VcF90b3AxMF9nZW5lc191bmlxdWUgPC0gdW5pcXVlKGVnb1VwX3RvcDEwX2dlbmVzKQojIHRhYmxlKGVnb1VwX3RvcDEwX2dlbmVzKQojIGVnb1VwX2dlbmVzQnlHcm91cCA8LSBhcy5kYXRhLmZyYW1lKHQocGx5cjo6bGRwbHkoZWdvVXBfdG9wMTBfZ2VuZXNfZ3JvdXAsIHJiaW5kKSkpCiMgY29sbmFtZXMoZWdvVXBfZ2VuZXNCeUdyb3VwKSA8LSBzb3J0ZWRfZWdvVXBfdG9wMTAkRGVzY3JpcHRpb24KIyBlZ29VcF9nZW5lc0J5R3JvdXBfaW9uT25seSA8LSBlZ29VcF9nZW5lc0J5R3JvdXBbLGMoMTo2LDg6MTApXQoKIyB3cml0ZS5jc3YoZWdvVXBfZ2VuZXNCeUdyb3VwLCBmaWxlPSJ0b3AxMEdPdGVybXNVcHJlZ3VsYXRlZF9nZW5lTWVtYmVyc2hpcC5jc3YiKQojIGlvbkdlbmVzIDwtIHVuaXF1ZSh1bmxpc3QoZWdvVXBfZ2VuZXNCeUdyb3VwX2lvbk9ubHkpKQojIAojIGVuc2VtYmwgPSB1c2VFbnNlbWJsKGJpb21hcnQ9ImVuc2VtYmwiLCBkYXRhc2V0PSJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiKQojIElEcyA8LSBhcy5jaGFyYWN0ZXIoaW9uR2VuZXMpCiMgZ2VuZVVwSUQgPC0gbmFtZXMoZ2VuZUxpc3RfdXApCiMgZ2VuZURvd25JRCA8LSBuYW1lcyhnZW5lTGlzdF9kb3duKQojIGdlbmVkZXNjX2lvbiA8LSBnZXRCTShhdHRyaWJ1dGVzPWMoJ2V4dGVybmFsX2dlbmVfbmFtZScsJ2Rlc2NyaXB0aW9uJyksIGZpbHRlcnMgPSAnZXh0ZXJuYWxfZ2VuZV9uYW1lJywgdmFsdWVzID0gSURzLCBtYXJ0ID1lbnNlbWJsKQojIHdyaXRlLmNzdihnZW5lZGVzY19pb24sIGZpbGUgPSAiaW9uQ2hhbm5lbEdlbmVzX2Rlc2NyaXB0aW9uLmNzdiIpCgojIGdlbmVkZXNjX1VwIDwtIGdldEJNKGF0dHJpYnV0ZXM9YygnZXh0ZXJuYWxfZ2VuZV9uYW1lJywnZGVzY3JpcHRpb24nKSwgZmlsdGVycyA9ICdleHRlcm5hbF9nZW5lX25hbWUnLCB2YWx1ZXMgPSBnZW5lVXBJRCwgbWFydCA9ZW5zZW1ibCkKIyB3cml0ZS5jc3YoZ2VuZWRlc2NfVXAsIGZpbGUgPSAidXByZWd1bGF0ZWRHZW5lc19kZXNjcmlwdGlvbi5jc3YiKQojIGdlbmVkZXNjX0Rvd24gPC0gZ2V0Qk0oYXR0cmlidXRlcz1jKCdleHRlcm5hbF9nZW5lX25hbWUnLCdkZXNjcmlwdGlvbicpLCBmaWx0ZXJzID0gJ2V4dGVybmFsX2dlbmVfbmFtZScsIHZhbHVlcyA9IGdlbmVEb3duSUQsIG1hcnQgPWVuc2VtYmwpCiMgd3JpdGUuY3N2KGdlbmVkZXNjX0Rvd24sIGZpbGUgPSAiZG93bnJndWxhdGVkR2VuZXNfZGVzY3JpcHRpb24uY3N2IikKYGBgCgoKYGBge3J9CmdlbmVMaXN0X2FsbCA8LSBhcy52ZWN0b3IocmVzdWx0c18wdG84ZCRsb2cyRm9sZENoYW5nZSkKbmFtZXMoZ2VuZUxpc3RfYWxsKSA8LSByb3duYW1lcyhyZXN1bHRzXzB0bzhkKQphIDwtIG5hbWVzKGdlbmVMaXN0X2FsbCkKZ2VuZXNfRU5UUkVaSUQgPC0gYml0cihhLCBmcm9tVHlwZSA9ICJTWU1CT0wiLCB0b1R5cGUgPSAiRU5UUkVaSUQiLCBPcmdEYiA9IE9yZ0RCKSRFTlRSRVpJRApuYW1lcyhnZW5lTGlzdF9hbGwpIDwtIGdlbmVzX0VOVFJFWklECgpnZW5lX2RmIDwtIGRhdGEuZnJhbWUoRW50cmV6PW5hbWVzKGdlbmVMaXN0X2FsbCksIEhHTkM9YSwgRkM9Z2VuZUxpc3RfYWxsKQpnZW5lX2RmIDwtIGdlbmVfZGZbYWJzKGdlbmVfZGYkRkMpID4gMSxdCmdlbmVfZGYkZ3JvdXAgPC0gInVwcmVndWxhdGVkIgpnZW5lX2RmJGdyb3VwW2dlbmVfZGYkRkMgPCAwXSA8LSAiZG93bnJlZ3VsYXRlZCIKZ2VuZV9kZiRvdGhlcmdyb3VwIDwtICJBIgpnZW5lX2RmJG90aGVyZ3JvdXBbYWJzKGdlbmVfZGYkRkMpID4gMl0gPC0gIkIiCgpmb3JtdWxhX3JlcyA8LSBjb21wYXJlQ2x1c3RlcihFbnRyZXp+Z3JvdXArb3RoZXJncm91cCwgZGF0YT1nZW5lX2RmLCBmdW49ImVucmljaEtFR0ciKQpoZWFkKGFzLmRhdGEuZnJhbWUoZm9ybXVsYV9yZXMpKQoKYGBgCiMjIyMgMy4gRXhwbG9yaW5nIFJlc3VsdHMKCmBgYHtyfQpwbG90TUEocmVzLCB5bGltPWMoLTIsMikpCnBsb3RDb3VudHMoZGRzLCBnZW5lPXdoaWNoLm1pbihyZXMkcGFkaiksIGludGdyb3VwPSJ0cmVhdG1lbnQiKQoKYGBgCgojIyMjIExvZyBub3JtYWxpemUgcmVzdWx0cyAjIyMjCmBgYHtyfQoKIyBub3JtYWxpemVkQ291bnRzIDwtIHQoIHQoY291bnRzKGRkcykpIC8gc2l6ZUZhY3RvcnMoZGRzKSApCgojbG9nMiBub3JtYWxpemVkIGNvdW50cwpybGQyIDwtIHJsb2coZGRzLCBibGluZCA9IEZBTFNFKQojIHNhdmUocmxkMiwgZmlsZSA9ICJSTEQyX1NDMSw3LDEwX1RpbWVjb3Vyc2VfaG1hcC5SRGF0YSIpCgojIGxvYWQoIlJMRDJfU0MxLDcsMTBfVGltZWNvdXJzZV9obWFwLlJEYXRhIikKYGBgCgojIyMjIENsdXN0ZXJpbmcgIyMjCgpgYGB7cn0Kc2FtcGxlRGlzdHMgPC0gZGlzdCh0KGFzc2F5KHJsZDIpKSkKc2FtcGxlRGlzdHMKCnNhbXBsZURpc3RNYXRyaXggPC0gYXMubWF0cml4KCBzYW1wbGVEaXN0cyApCnJvd25hbWVzKHNhbXBsZURpc3RNYXRyaXgpIDwtIHBhc3RlKHJsZDIkdHJlYXRtZW50LCBybGQyJGNlbGwsIHNlcCA9ICIgLSAiICkKY29sbmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gTlVMTApjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQpwaGVhdG1hcChzYW1wbGVEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBzYW1wbGVEaXN0cywKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gc2FtcGxlRGlzdHMsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCgpwb2lzZCA8LSBQb2lDbGFDbHU6OlBvaXNzb25EaXN0YW5jZSh0KGNvdW50cyhkZHMpKSkKc2FtcGxlUG9pc0Rpc3RNYXRyaXggPC0gYXMubWF0cml4KCBwb2lzZCRkZCApCnJvd25hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBwYXN0ZSggZGRzJGRleCwgZGRzJGNlbGwsIHNlcD0iIC0gIiApCmNvbG5hbWVzKHNhbXBsZVBvaXNEaXN0TWF0cml4KSA8LSBOVUxMCnBoZWF0bWFwKHNhbXBsZVBvaXNEaXN0TWF0cml4LAogICAgICAgICBjbHVzdGVyaW5nX2Rpc3RhbmNlX3Jvd3MgPSBwb2lzZCRkZCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gcG9pc2QkZGQsCiAgICAgICAgIGNvbCA9IGNvbG9ycykKCm1kcyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMikpICAlPiUKICAgICAgICAgY2JpbmQoY21kc2NhbGUoc2FtcGxlRGlzdE1hdHJpeCkpCmdncGxvdChtZHMsIGFlcyh4ID0gYDFgLCB5ID0gYDJgLCBjb2xvciA9IGNlbGwsIHNoYXBlID0gdHJlYXRtZW50KSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMpICsgY29vcmRfZml4ZWQoKSArIHRoZW1lX2J3KCkgKwogIHhsYWIoIlBDMSIpICsgeWxhYigiUEMyIikgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksIAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwKICAgICAgICAjIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGl0bGU9ZWxlbWVudF90ZXh0KHNpemU9MTIpKQoKIyBsaWJyYXJ5KCJnZW5lZmlsdGVyIikKdG9wVmFyR2VuZXMgPC0gaGVhZChvcmRlcihyb3dWYXJzKGFzc2F5KHJsZDIpKSwgZGVjcmVhc2luZyA9IFRSVUUpLCA1MDAwKQptYXQgIDwtIGFzc2F5KHJsZDIpWyB0b3BWYXJHZW5lcywgXQptYXQgIDwtIG1hdCAtIHJvd01lYW5zKG1hdCkKYW5ubyA8LSBhcy5kYXRhLmZyYW1lKGNvbERhdGEocmxkMilbLCBjKCJjZWxsIiwidHJlYXRtZW50IildKQpuYW1lcyhhbm5vKSA8LSBjKCJDZWxsIiwgIlRyZWF0bWVudCIpCmFubm90YXRpb25fY29sb3JzID0gbGlzdCgKICBDZWxsID0gYyhTQzAxPSJyZWQyIiwgU0MwNz0iZ3JlZW4yIiwgU0MxMD0iYmx1ZTIiKSwKICBUcmVhdG1lbnQgPSBjKCIwIj0iY3lhbjIiLCAiMyI9ImRhcmtvcmFuZ2UiLCAiOCI9ImRhcmtvcmNoaWQiKSkKcGhlYXRtYXAobWF0LCBhbm5vdGF0aW9uX2NvbCA9IGFubm8sIHNob3dfcm93bmFtZXMgPSBGLCBzaG93X2NvbG5hbWVzID0gRiwKICAgICAgICAgYW5ub3RhdGlvbl9jb2xvcnMgPSBhbm5vdGF0aW9uX2NvbG9ycykKYGBgCgojIyMjIFRpbWUgc2VyaWVzIGFuYWx5c2lzICMjIyMKCiMgMSBERVNlcTIgdGltZSBzZXJpZXMgYW5hbHlzaXMKYGBge3J9CiMgYnJvd3NlVmlnbmV0dGVzKCJybmFzZXFHZW5lIikKCmRkc1RDIDwtIERFU2VxKGRkcywgdGVzdD0iTFJUIiwgcmVkdWNlZCA9IH4gY2VsbCArIHRyZWF0bWVudCkKcmVzVEMgPC0gcmVzdWx0cyhkZHNUQykKcmVzVEMkc3ltYm9sIDwtIG1jb2xzKGRkc1RDKSRzeW1ib2wKIyBoZWFkKHJlc1RDW29yZGVyKHJlc1RDJHBhZGopLF0sIDQpCgp0YyA8LSBwbG90Q291bnRzKGRkc1RDLCB3aGljaC5taW4ocmVzVEMkcGFkaiksIAogICAgICAgICAgICAgICAgICAgaW50Z3JvdXAgPSBjKCJ0cmVhdG1lbnQiLCJjZWxsIiksIHJldHVybkRhdGEgPSBUUlVFKQoKZGRzVENbd2hpY2gubWluKHJlc1RDJHBhZGopLF0KCmdncGxvdCh0YywKICBhZXMoeCA9IHJlcChjKDAsMyw4KSwgZWFjaD05KSwgeSA9IGNvdW50LCBjb2xvciA9IGNlbGwsIGdyb3VwID0gY2VsbCkpICsgCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoc2UgPSBGQUxTRSwgbWV0aG9kID0gImxvZXNzIikgKyBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lX2J3KCkgKwogIGdndGl0bGUoIlRpbWUgQ291cnNlIEV4cHJlc3Npb24gb2YgUERLNCIpICsKICBsYWJzKHg9IlRpbWUgKGRheXMpIiwgeT0iR2VuZSBDb3VudCIpICsKICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpLCAKICAgICAgICBheGlzLnRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEyLGZhY2U9ImJvbGQiKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xMiwgZmFjZT0iYm9sZCIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKcmVzdWx0c05hbWVzKGRkc1RDKQoKYmV0YXMgPC0gY29lZihkZHNUQykKY29sbmFtZXMoYmV0YXMpCgp0b3BHZW5lcyA8LSBoZWFkKG9yZGVyKHJlc1RDJHBhZGopLDUwKQptYXQgPC0gYmV0YXNbdG9wR2VuZXMsIC1jKDEsMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKcGhlYXRtYXAobWF0LCBicmVha3M9c2VxKGZyb209LXRociwgdG89dGhyLCBsZW5ndGg9MTAxKSwKICAgICAgICAgY2x1c3Rlcl9jb2w9RkFMU0UpCmBgYAoKCiMjIyBOT1RFCk9yaWdpbmFsIGNvZGUgYmVsb3cgcHJvZHVjZWQgbWFueSBtZXNzYWdlcyBvZiBgTm8gaWQgdmFyaWFibGVzOyB1c2luZyBhbGwgYXMgbWVhc3VyZSB2YXJpYWJsZXNgOyBwcmVzdW1hYmx5IGEgbGluZSBmb3IgZWFjaCBnZW5lLiBUaGlzIGlzIGR1ZSB0byB0aGUgYG1lbHRgIGZ1bmN0aW9uIG5vdCBoYXZpbmcgYW55IGlkIHZhcmlhYmxlcyB0byB1c2UuICAKCgpSZWppZ2dlcmluZyBjb2RlIG5vdCB5ZXQgZmluaXNoZWQuICBTaG91bGQgcHJvYmFibHkgdXNlIApgYGB7ciBTdWJsaW5lIEFOT1ZBLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQojIDEuMSBBTk9WQSAtIGNvbXBhcmUgYnR3biBzdWJsaW5lcyAKIyBncm91cCA8LSBhcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX2Jhc2VsaW5lIDwtIGxpc3QoKQpUdWtleVNDMDd0b1NDMDEgPC0gbGlzdCgpClR1a2V5U0MxMHRvU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzEwdG9TQzA3IDwtIGxpc3QoKQpub3JtX2RhdGEgPC0gYXMuZGF0YS5mcmFtZShhc3NheShybGQyKSlbYygnU0MwMV9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTBfcmVwMycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDEnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5MF9yZXAyJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMycpXQoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyMjIE5ldyBjb2RlIGJ5IERSVCAjIyMKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHNhbXBfbmFtZXMgPC0gY29sbmFtZXMobm9ybV9kYXRhKQoKIyBjb21wYXJlU3ViY2xvbmVzIDwtIGZ1bmN0aW9uKGdlbmVfbmFtZSwgZGF0PW5vcm1fZGF0YSwgc2FtcF9uYW1lcz1OVUxMLCBncm91cD1OVUxMKQojIHsKIyAgICAgaWYoaXMubnVsbChncm91cCkpIGdyb3VwIDwtIGFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyAgICAgaWYoaXMubnVsbChzYW1wX25hbWVzKSkgc2FtcF9uYW1lcyA8LSBjb2xuYW1lcyhkYXQpCiMgICAgICMgZGZhID0gZGF0YSBmb3IgYW5hbHlzaXMKIyAgICAgZGZhIDwtIGRhdGEuZnJhbWUodmFsdWU9YXMubnVtZXJpYyh0KGRhdFtnZW5lX25hbWUsXSkpLCBncm91cD1ncm91cCkKIyAgICAgcm93bmFtZXMoZGZhKSA8LSBzYW1wX25hbWVzCiMgICAgIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGRmYSkKIyAgICAgYW5vdmFfYmFzZWxpbmUgPC0gc3VtbWFyeShmaXQpW1sxXV1bWydQcig+RiknXV1bMV0KIyAgICAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQojICAgICBwdmFsIDwtIGRhdGEuZnJhbWUocF9hZGo9cmVzdWx0cyRncm91cFssJ3AgYWRqJ10pCiMgICAgIHJvd25hbWVzKHB2YWwpIDwtIGMoIlR1a2V5U0MwN3RvU0MwMSIsIlR1a2V5U0MxMHRvU0MwMSIsIlR1a2V5U0MxMHRvU0MwNyIpCiMgICAgIG91dCA8LSBsaXN0KGFub3ZhX2Jhc2VsaW5lID0gYW5vdmFfYmFzZWxpbmUsCiMgICAgICAgICAgICAgICAgIHB2YWwgPSBwdmFsKQojICAgICAjIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQojICAgICAjIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQojICAgICAjIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXSAgICAKIyAgICAgcmV0dXJuKG91dCkKIyB9CgojIHRlbXAgPC0gbGFwcGx5KHJvd25hbWVzKG5vcm1fZGF0YSksIGNvbXBhcmVTdWJjbG9uZXMpCiMgYW5vdmFfcHZhbCA8LSBzYXBwbHkodGVtcCwgIltbIiwgMSkKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMgRW5kIG5ldyBjb2RlICMjIwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpmb3IgKGdlbmUgaW4gMTpucm93KG5vcm1fZGF0YSkpIHsKICBnZW5lX25vcm1fZGF0YSA8LSBub3JtX2RhdGFbZ2VuZSxdCiAgIyBkMyA8LSBkYXRhLmZyYW1lKHkgPSBnZW5lX25vcm1fZGF0YSwgZ3JvdXAgPSBncm91cCkKICAjIGZpdCA8LSBsbSh5fmdyb3VwLCBkMykKICBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIGRhdGEuZnJhbWUodmFyaWFibGU9Y29sbmFtZXMoZ2VuZV9ub3JtX2RhdGEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZT1hcy5udW1lcmljKHQoZ2VuZV9ub3JtX2RhdGEpKSkKICBnZW5lX25vcm1fZGF0YV9tZWx0JGdyb3VwIDwtIGdyb3VwCiAgZml0IDwtIGFvdih2YWx1ZX5ncm91cCwgZ2VuZV9ub3JtX2RhdGFfbWVsdCkKICAjIGFub3ZhX2xpc3RbZ2VuZV0gPC0gYW5vdmEoZml0KSQnUHIoPkYpJ1sxXQogIGFub3ZhX2Jhc2VsaW5lW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MwN3RvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMHRvU0MwMVtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMHRvU0MwN1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKCiAgCmFub3ZhX3B2YWwgPC0gdW5saXN0KGFub3ZhX2Jhc2VsaW5lKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzA3dG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MwN3RvU0MwMSkKVHVrZXlTQzEwdG9TQzAxX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwMSkKVHVrZXlTQzEwdG9TQzA3X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMHRvU0MwNykKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBhbm92YV9wdmFsKQpub3JtX2RhdGFfc3RhdHMgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzLCBUdWtleVNDMDd0b1NDMDFfcHZhbCkKbm9ybV9kYXRhX3N0YXRzIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0cywgVHVrZXlTQzEwdG9TQzAxX3B2YWwpCm5vcm1fZGF0YV9zdGF0cyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHMsIFR1a2V5U0MxMHRvU0MwN19wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0cywgZmlsZSA9ICJzdWJjbG9uZUNvdW50c19hbm92YV90dWtleV9ERVNlcTIuUkRhdGEiKQoKIyBJZGVudGlmeSBnZW5lcyB0aGF0IGRpZmZlciBiZXR3ZWVuIGNsb25lcyBiYXNlZCBvbiAKIyBBTk9WQSBwLXZhbHVlIHdpdGggZGVmaW5lZCB0aHJlc2hvbGQKc2lnVGhyZXNoIDwtIDAuMDUKdGFibGUoYW5vdmFfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3dG9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMHRvU0MwMV9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTB0b1NDMDdfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c1siYW5vdmFfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGwgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzWyJhbm92YV9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNbIlR1a2V5U0MwN3RvU0MwMV9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c1siVHVrZXlTQzEwdG9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzWyJUdWtleVNDMTB0b1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c1tzaWdJbmRlY2llcyxdKQpzaWdEaWZmR2VuZXNBbGwgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzW3NpZ0luZGVjaWVzQWxsLF0pCmBgYAoKIyAyLiBBTk9WQSBidHduIHRpbWUgcG9pbnRzICYgc2hhcmVkIGJ0d24gc3VibGluZXMpCmBgYHtyIFNDMDEgdGltZSBBTk9WQSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZXZhbD1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MwMSA8LSBsaXN0KCkKVHVrZXlTQzAxX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMDFfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MwMV90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMDF0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMDFfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDFfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzAxX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwMV9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MwMXRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMDF0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgIyBnZW5lX25vcm1fZGF0YV9tZWx0IDwtIG1lbHQoZ2VuZV9ub3JtX2RhdGEpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCgogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwMVtnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDFfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDFfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDFfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDFfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwMSkgIyBtYWtlIGFycmF5ClR1a2V5U0MwMV90aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTApClR1a2V5U0MwMV90aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTMpClR1a2V5U0MwMV90aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDFfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDF0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgYW5vdmFfU0MwMV9wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwMSA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwMSwgVHVrZXlTQzAxX3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzAxIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBUdWtleVNDMDFfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDEgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDEsIFR1a2V5U0MwMV90aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzAxLCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzAxdGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzAxX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwMV90aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDFfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzAxX3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzAxIDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzAxWyJhbm92YV9TQzAxX3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDEgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDFbImFub3ZhX1NDMDFfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDFbIlR1a2V5U0MwMV90aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzAxWyJUdWtleVNDMDFfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwMVsiVHVrZXlTQzAxX3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzAxIDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzAxW3NpZ0luZGVjaWVzX1NDMDEsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDEgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDFbc2lnSW5kZWNpZXNBbGxfU0MwMSxdKQpgYGAKCgpgYGB7ciBTQzA3IHRpbWUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGV2YWw9RkFMU0V9Cmdyb3VwPC1hcy5mYWN0b3IoYygxLDEsMSwyLDIsMiwzLDMsMykpCiMgR2V0dGluZyBhbm92YSB2YWx1ZXMgZm9yIGVhY2ggZ2VuZSBpbiBkYXRhc2V0CmFub3ZhX1NDMDcgPC0gbGlzdCgpClR1a2V5U0MwN190aW1lMCA8LSBsaXN0KCkKVHVrZXlTQzA3X3RpbWUzIDwtIGxpc3QoKQpUdWtleVNDMDdfdGltZTggPC0gbGlzdCgpCm5vcm1fZGF0YV9TQzA3dGltZSA8LSBhcy5kYXRhLmZyYW1lKGFzc2F5KHJsZDIpKVtjKCdTQzA3X2RheTBfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkwX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5MF9yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheTNfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXkzX3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5M19yZXAzJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzA3X2RheThfcmVwMScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MwN19kYXk4X3JlcDInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMDdfZGF5OF9yZXAzJyldCmZvciAoZ2VuZSBpbiAxOm5yb3cobm9ybV9kYXRhX1NDMDd0aW1lKSkgewogIGdlbmVfbm9ybV9kYXRhIDwtIG5vcm1fZGF0YV9TQzA3dGltZVtnZW5lLF0KICAjIGQzIDwtIGRhdGEuZnJhbWUoeSA9IGdlbmVfbm9ybV9kYXRhLCBncm91cCA9IGdyb3VwKQogICMgZml0IDwtIGxtKHl+Z3JvdXAsIGQzKQogICMgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBtZWx0KGdlbmVfbm9ybV9kYXRhKQogIGdlbmVfbm9ybV9kYXRhX21lbHQgPC0gZGF0YS5mcmFtZSh2YXJpYWJsZT1jb2xuYW1lcyhnZW5lX25vcm1fZGF0YSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlPWFzLm51bWVyaWModChnZW5lX25vcm1fZGF0YSkpKQogIGdlbmVfbm9ybV9kYXRhX21lbHQkZ3JvdXAgPC0gZ3JvdXAKICBmaXQgPC0gYW92KHZhbHVlfmdyb3VwLCBnZW5lX25vcm1fZGF0YV9tZWx0KQogICMgYW5vdmFfbGlzdFtnZW5lXSA8LSBhbm92YShmaXQpJCdQcig+RiknWzFdCiAgYW5vdmFfU0MwN1tnZW5lXSA8LSBzdW1tYXJ5KGZpdClbWzFdXVtbJ1ByKD5GKSddXVsxXQogIHJlc3VsdHMgPC0gVHVrZXlIU0QoZml0LCBjb25mLmxldmVsID0gMC45NSkKICBUdWtleVNDMDdfdGltZTBbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMV0KICBUdWtleVNDMDdfdGltZTNbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bMl0KICBUdWtleVNDMDdfdGltZThbZ2VuZV0gPC0gcmVzdWx0cyRncm91cFssJ3AgYWRqJ11bM10KICAKfQojIHByaW50KGFub3ZhX2xpc3QpCmFub3ZhX1NDMDdfcHZhbCA8LSB1bmxpc3QoYW5vdmFfU0MwNykgIyBtYWtlIGFycmF5ClR1a2V5U0MwN190aW1lMF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTApClR1a2V5U0MwN190aW1lM19wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTMpClR1a2V5U0MwN190aW1lOF9wdmFsIDwtIHVubGlzdChUdWtleVNDMDdfdGltZTgpCgojIE1ha2UgbWFzdGVyIGRhdGFzZXQgd2l0aCBzdGF0aXN0aWNzCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGFzLmRhdGEuZnJhbWUobm9ybV9kYXRhX1NDMDd0aW1lKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgYW5vdmFfU0MwN19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MwNyA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MwNywgVHVrZXlTQzA3X3RpbWUwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzA3IDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBUdWtleVNDMDdfdGltZTNfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMDcgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMDcsIFR1a2V5U0MwN190aW1lOF9wdmFsKQoKIyBzYXZlKG5vcm1fZGF0YV9zdGF0c19TQzA3LCBmaWxlID0gInN1YmNsb25lQ291bnRzX2Fub3ZhX3R1a2V5X0RFU2VxMl9TQzA3dGltZS5SRGF0YSIpCgojIElkZW50aWZ5IGdlbmVzIHRoYXQgZGlmZmVyIGJldHdlZW4gY2xvbmVzIGJhc2VkIG9uIAojIEFOT1ZBIHAtdmFsdWUgd2l0aCBkZWZpbmVkIHRocmVzaG9sZApzaWdUaHJlc2ggPC0gMC4wNQp0YWJsZShhbm92YV9TQzA3X3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MwN190aW1lMF9wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMDdfdGltZTNfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzA3X3RpbWU4X3B2YWwgPCBzaWdUaHJlc2gpCgpzaWdJbmRlY2llc19TQzA3IDwtIHdoaWNoKG5vcm1fZGF0YV9zdGF0c19TQzA3WyJhbm92YV9TQzA3X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzQWxsX1NDMDcgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMDdbImFub3ZhX1NDMDdfcHZhbCJdIDwgc2lnVGhyZXNoICYgCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMDdbIlR1a2V5U0MwN190aW1lMF9wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzA3WyJUdWtleVNDMDdfdGltZTNfcHZhbCJdIDwgc2lnVGhyZXNoICYKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MwN1siVHVrZXlTQzA3X3RpbWU4X3B2YWwiXSA8IHNpZ1RocmVzaCkKCnNpZ0RpZmZHZW5lc19TQzA3IDwtIHJvd25hbWVzKG5vcm1fZGF0YV9zdGF0c19TQzA3W3NpZ0luZGVjaWVzX1NDMDcsXSkKc2lnRGlmZkdlbmVzQWxsX1NDMDcgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMDdbc2lnSW5kZWNpZXNBbGxfU0MwNyxdKQpgYGAKCmBgYHtyIFNDMTAgdGltZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ3JvdXA8LWFzLmZhY3RvcihjKDEsMSwxLDIsMiwyLDMsMywzKSkKIyBHZXR0aW5nIGFub3ZhIHZhbHVlcyBmb3IgZWFjaCBnZW5lIGluIGRhdGFzZXQKYW5vdmFfU0MxMCA8LSBsaXN0KCkKVHVrZXlTQzEwX3RpbWUwIDwtIGxpc3QoKQpUdWtleVNDMTBfdGltZTMgPC0gbGlzdCgpClR1a2V5U0MxMF90aW1lOCA8LSBsaXN0KCkKbm9ybV9kYXRhX1NDMTB0aW1lIDwtIGFzLmRhdGEuZnJhbWUoYXNzYXkocmxkMikpW2MoJ1NDMTBfZGF5MF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTBfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkwX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5M19yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheTNfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXkzX3JlcDMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1NDMTBfZGF5OF9yZXAxJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTQzEwX2RheThfcmVwMicsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU0MxMF9kYXk4X3JlcDMnKV0KZm9yIChnZW5lIGluIDE6bnJvdyhub3JtX2RhdGFfU0MxMHRpbWUpKSB7CiAgZ2VuZV9ub3JtX2RhdGEgPC0gbm9ybV9kYXRhX1NDMTB0aW1lW2dlbmUsXQogICMgZDMgPC0gZGF0YS5mcmFtZSh5ID0gZ2VuZV9ub3JtX2RhdGEsIGdyb3VwID0gZ3JvdXApCiAgIyBmaXQgPC0gbG0oeX5ncm91cCwgZDMpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCA8LSBkYXRhLmZyYW1lKHZhcmlhYmxlPWNvbG5hbWVzKGdlbmVfbm9ybV9kYXRhKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWU9YXMubnVtZXJpYyh0KGdlbmVfbm9ybV9kYXRhKSkpCiAgZ2VuZV9ub3JtX2RhdGFfbWVsdCRncm91cCA8LSBncm91cAogIGZpdCA8LSBhb3YodmFsdWV+Z3JvdXAsIGdlbmVfbm9ybV9kYXRhX21lbHQpCiAgIyBhbm92YV9saXN0W2dlbmVdIDwtIGFub3ZhKGZpdCkkJ1ByKD5GKSdbMV0KICBhbm92YV9TQzEwW2dlbmVdIDwtIHN1bW1hcnkoZml0KVtbMV1dW1snUHIoPkYpJ11dWzFdCiAgcmVzdWx0cyA8LSBUdWtleUhTRChmaXQsIGNvbmYubGV2ZWwgPSAwLjk1KQogIFR1a2V5U0MxMF90aW1lMFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsxXQogIFR1a2V5U0MxMF90aW1lM1tnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVsyXQogIFR1a2V5U0MxMF90aW1lOFtnZW5lXSA8LSByZXN1bHRzJGdyb3VwWywncCBhZGonXVszXQogIAp9CiMgcHJpbnQoYW5vdmFfbGlzdCkKYW5vdmFfU0MxMF9wdmFsIDwtIHVubGlzdChhbm92YV9TQzEwKSAjIG1ha2UgYXJyYXkKVHVrZXlTQzEwX3RpbWUwX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMCkKVHVrZXlTQzEwX3RpbWUzX3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lMykKVHVrZXlTQzEwX3RpbWU4X3B2YWwgPC0gdW5saXN0KFR1a2V5U0MxMF90aW1lOCkKCiMgTWFrZSBtYXN0ZXIgZGF0YXNldCB3aXRoIHN0YXRpc3RpY3MKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gYXMuZGF0YS5mcmFtZShub3JtX2RhdGFfU0MxMHRpbWUpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBhbm92YV9TQzEwX3B2YWwpCm5vcm1fZGF0YV9zdGF0c19TQzEwIDwtIGNiaW5kKG5vcm1fZGF0YV9zdGF0c19TQzEwLCBUdWtleVNDMTBfdGltZTBfcHZhbCkKbm9ybV9kYXRhX3N0YXRzX1NDMTAgPC0gY2JpbmQobm9ybV9kYXRhX3N0YXRzX1NDMTAsIFR1a2V5U0MxMF90aW1lM19wdmFsKQpub3JtX2RhdGFfc3RhdHNfU0MxMCA8LSBjYmluZChub3JtX2RhdGFfc3RhdHNfU0MxMCwgVHVrZXlTQzEwX3RpbWU4X3B2YWwpCgojIHNhdmUobm9ybV9kYXRhX3N0YXRzX1NDMTAsIGZpbGUgPSAic3ViY2xvbmVDb3VudHNfYW5vdmFfdHVrZXlfREVTZXEyX1NDMTB0aW1lLlJEYXRhIikKCiMgSWRlbnRpZnkgZ2VuZXMgdGhhdCBkaWZmZXIgYmV0d2VlbiBjbG9uZXMgYmFzZWQgb24gCiMgQU5PVkEgcC12YWx1ZSB3aXRoIGRlZmluZWQgdGhyZXNob2xkCnNpZ1RocmVzaCA8LSAwLjA1CnRhYmxlKGFub3ZhX1NDMTBfcHZhbCA8IHNpZ1RocmVzaCkKdGFibGUoVHVrZXlTQzEwX3RpbWUwX3B2YWwgPCBzaWdUaHJlc2gpCnRhYmxlKFR1a2V5U0MxMF90aW1lM19wdmFsIDwgc2lnVGhyZXNoKQp0YWJsZShUdWtleVNDMTBfdGltZThfcHZhbCA8IHNpZ1RocmVzaCkKCnNpZ0luZGVjaWVzX1NDMTAgPC0gd2hpY2gobm9ybV9kYXRhX3N0YXRzX1NDMTBbImFub3ZhX1NDMTBfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnSW5kZWNpZXNBbGxfU0MxMCA8LSB3aGljaChub3JtX2RhdGFfc3RhdHNfU0MxMFsiYW5vdmFfU0MxMF9wdmFsIl0gPCBzaWdUaHJlc2ggJiAKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtX2RhdGFfc3RhdHNfU0MxMFsiVHVrZXlTQzEwX3RpbWUwX3B2YWwiXSA8IHNpZ1RocmVzaCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgbm9ybV9kYXRhX3N0YXRzX1NDMTBbIlR1a2V5U0MxMF90aW1lM19wdmFsIl0gPCBzaWdUaHJlc2ggJgogICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm1fZGF0YV9zdGF0c19TQzEwWyJUdWtleVNDMTBfdGltZThfcHZhbCJdIDwgc2lnVGhyZXNoKQoKc2lnRGlmZkdlbmVzX1NDMTAgPC0gcm93bmFtZXMobm9ybV9kYXRhX3N0YXRzX1NDMTBbc2lnSW5kZWNpZXNfU0MxMCxdKQpzaWdEaWZmR2VuZXNBbGxfU0MxMCA8LSByb3duYW1lcyhub3JtX2RhdGFfc3RhdHNfU0MxMFtzaWdJbmRlY2llc0FsbF9TQzEwLF0pCmBgYAoKYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KCmFsbF9TQ3NfdGltZSA8LSBSZWR1Y2UoaW50ZXJzZWN0LCAKICAgICAgICAgICAgICAgICAgICAgICBsaXN0KHNpZ0RpZmZHZW5lc0FsbF9TQzAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lnRGlmZkdlbmVzQWxsX1NDMDcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaWdEaWZmR2VuZXNBbGxfU0MxMCkpCgpkZl9hbGxTQ3NfdGltZSA8LSBkYXRhLmZyYW1lKGdlbmUgPSBhbGxfU0NzX3RpbWUpCiMgZ2VuZXMgPC0gZGZfYWxsU0NzX3RpbWUkZ2VuZQojIEdfbGlzdCA8LSBnZXRCTShmaWx0ZXJzPSAiZW5zZW1ibF9nZW5lX2lkIiwgYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSx2YWx1ZXM9Z2VuZXMsbWFydD1tYXJ0KQptZXJnZShkZl9hbGxTQ3NfdGltZSxHX2xpc3QsYnkueD0iZ2VuZSIsYnkueT0iZW5zZW1ibF9nZW5lX2lkIikKCiMgd3JpdGUuY3N2KEdfbGlzdCwgZmlsZSA9ICJBTk9WQV9hbGxTQ3NUaW1lX3NoYXJlZF9nZW5lcy5jc3YiKQoKIyBDb21wYXJlIGdlbmUgbGlzdHMgZm9yIGJldHdlZW4gc3VibGluZXMgYW5kIHRpbWUKIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKZGJzIDwtIGxpc3RFbnJpY2hyRGJzKCkKZGJzIDwtIGMoIktFR0dfMjAxNiIsIAogICAgICAgICAiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTgiKQoKZW5yaWNoZWRfYWxsU0NzdGltZSA8LSBlbnJpY2hyKEdfbGlzdCRoZ25jX3N5bWJvbCwgZGJzKQoKS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUgPC0gZW5yaWNoZWRfYWxsU0NzdGltZVtbIktFR0dfMjAxNiJdXVsxOjUsXQpLRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtcyA8LSBmYWN0b3IoS0VHR191cHJlZ19hbGxTQ3N0aW1lX3RvcDUkVGVybSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1LRUdHX3VwcmVnX2FsbFNDc3RpbWVfdG9wNSRUZXJtKQoKZ2dwbG90KEtFR0dfdXByZWdfYWxsU0NzdGltZV90b3A1LCAKICAgICAgIGFlcyh4PVRlcm1zLCB5PS1sb2cxMChBZGp1c3RlZC5QLnZhbHVlKSkpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScpICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeCA9ICJQYXRod2F5IFRlcm0iLCB5ID0gIi1sb2cxMChxLXZhbHVlKSIpICArCiAgdGhlbWVfYncoKSAgKyBnZ3RpdGxlKCJQYXRod2F5IEVucmljaG1lbnQgLSBLRUdHIDIwMTYiKSArCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiKSwgCiAgICAgICAgYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEyKSwgCiAgICAgICAgYXhpcy50aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNCxmYWNlPSJib2xkIikpCiAgCgpgYGAKCiMgMyBKYWNrJ3MgbWV0aG9kIApgYGB7ciBldmFsPUZBTFNFfQojR3JhYiBhbGwgdGhlIG5hbWVzIGZyb20gcmVzIGluIHRoZSBERVNlcSBtYXRyaXgKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCgpjb3VudE1BVCA8LSBkYXRhLmZyYW1lKG5vcm1hbGl6ZWRDb3VudHNbdG9wR2VuZXMsXSkKCnN1YnJsID0gZGF0YS5mcmFtZShhc3NheShybGQyKSkKcmxNQVQgPSBkYXRhLmZyYW1lKHN1YnJsW3RvcEdlbmVzLF0pCgojTGFiZWxpbmcgcm93cyB3aXRoIEVOU0cgSURzCiMgY291bnRNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKGNvdW50TUFUKQojIGNvdW50TUFUJHBhZGogPSByZXNbdG9wR2VuZXMsInBhZGoiXQoKcmxNQVQkZW5zZW1ibF9nZW5lX2lkID0gcm93Lm5hbWVzKHJsTUFUKQpybE1BVCRwYWRqID0gcmVzW3RvcEdlbmVzLCJwYWRqIl0KCiMgbGlicmFyeShiaW9tYVJ0KQojIGVuc2VtYmwgPC0gdXNlTWFydCgiZW5zZW1ibCIpCiMgbWFydCA8LSB1c2VEYXRhc2V0KCJoc2FwaWVuc19nZW5lX2Vuc2VtYmwiLCBtYXJ0ID0gZW5zZW1ibCkKIyBnZW5lcyA9IHJvdy5uYW1lcyhybE1BVCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKI0NoZWNrIGlmIGRhdGEgZml0cyBhIG5vcm1hbCBkaXN0cmlidXRpb24KIyBwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgoY291bnRNQVRbLDE6MjddKSkpKQpwbG90KGRlbnNpdHkoYyhhcy5tYXRyaXgocmxNQVRbLDE6MjddKSkpKQoKCiNybE1BVCBmb2xsb3dzIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiwgdGhlcmVmb3JlIHdlIHdpbGwgdXNlIHRoaXMgaW4gdGhlIGhlYXRtYXAgY29uc3RydWN0aW9uCiNMYWJlbGluZyBkZiB3aXRoIGhnbmMgc3ltYm9scwpHRV9kYXRhIDwtIG1lcmdlKEdfbGlzdCwgcmxNQVQsIGJ5ID0gImVuc2VtYmxfZ2VuZV9pZCIpCgojTWFraW5nIHJvd25hbWVzIHVuaXF1ZSBoZ25jIHN5bWJvbHMKcm93bmFtZXMoR0VfZGF0YSkgPC0gbWFrZS5uYW1lcyhHRV9kYXRhWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI0F2ZXJhZ2luZyBybGQgYmV0d2VlbiB0cmlhbHMKQWNvbCA8LSBjKCJTQzAxX2RheTAiLAogICAgICAgICAgIlNDMDFfZGF5MyIsCiAgICAgICAgICAiU0MwMV9kYXk4IiwKICAgICAgICAgICJTQzA3X2RheTAiLAogICAgICAgICAgIlNDMDdfZGF5MyIsCiAgICAgICAgICAiU0MwN19kYXk4IiwKICAgICAgICAgICJTQzEwX2RheTAiLAogICAgICAgICAgIlNDMTBfZGF5MyIsCiAgICAgICAgICAiU0MxMF9kYXk4IikKZm9yKGkgaW4gMTpsZW5ndGgoQWNvbCkpewogIGogPSAyK2kKICBrID0gMiszKmkKICBHRV9kYXRhWyxBY29sW2ldXSA9IHJvd01lYW5zKEdFX2RhdGFbLGMoajprKV0pCn0KCgojQ2FsY3VsYXRpbmcgZm9sZCBjaGFuZ2VzIGFjcm9zcyBjb25kaXRpb25zIGluIGEgdHJpYW5ndWxhciBtYXRyaXggZm9ybQpHRV9tZWFuID0gR0VfZGF0YVssYygxLDIsMzA6MzkpXQpERVByb2MgPSBHRV9tZWFuCnN0YXJ0Y29sID0gNAplbmRjb2wgPSAxMgoKYWxsRkMgPC0gZnVuY3Rpb24oREVQcm9jLHN0YXJ0Y29sLGVuZGNvbCl7IAogIEdFX2ZvbGQgPSBERVByb2NbLC1jKHN0YXJ0Y29sOmVuZGNvbCldCiAgY29sdmVjID0gY29sbmFtZXMoREVQcm9jKVtzdGFydGNvbDplbmRjb2xdCiAgCiAgI0xhc3QgaW5kZXggaXMgYSBzZWxmIGNvbXBhcmlzb24gYW5kIGlzIHJlbW92ZWQKICBmb3IoayBpbiAxOihsZW5ndGgoY29sdmVjKS0xKSl7CiAgICAjU3RhcnQgd2l0aCBjb2x1bW4gdGhhdCBpcyAxIGF3YXkgZnJvbSBpbmRleCAKICAgIGZvcihqIGluIChrKzEpOmxlbmd0aChjb2x2ZWMpKXsKICAgICAgY29tcG5hbSA9IHBhc3RlMChjb2x2ZWNbal0sIi8iLGNvbHZlY1trXSkKICAgICAgI0xvb3AgdGhyb3VnaCBlYWNoIGdlbmUvcm93ICAKICAgICAgZm9yKGkgaW4gMTpucm93KERFUHJvYykpewogICAgICAgIGYgPSBERVByb2NbaSxjb2x2ZWNbal1dCiAgICAgICAgaCA9IERFUHJvY1tpLGNvbHZlY1trXV0KICAgICAgICAKICAgICAgICAjQ2FwdHVyZSB1cHJlZ3VsYXRpb24gYW5kIGRvd24gcmVndWxhdGlvbgogICAgICAgIGlmKGY+aCl7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAyXihmLWgpCiAgICAgICAgfWVsc2V7CiAgICAgICAgICBHRV9mb2xkW2ksY29tcG5hbV0gPSAtMl4oaC1mKQogICAgICAgIH0KICAgICAgICAKICAgICAgfQogICAgfQogIH0KICAKICByZXR1cm4oR0VfZm9sZCkKICAKfQoKI1N1YnNldCBnZW5lLCB0aGVuIHBsb3QsIHRoZW4gc2F2ZSBwbG90CiNQZXJoYXBzIG1ha2UgaGVhdG1hcHMgd2l0aCBzY2FsZWQgeiBzY29yZXMKI0lzIHRoZXJlIGEgd2F5IHRvIGNvbnNvbGlkYXRlIHJlcGxpY2F0ZSB6IHNjb3Jlcz8gR2VvbWV0cmljIG1lYW4/IAojUmVndWxhciBtZWFuLCB0aGVuIHNjYWxlLgoKIyBJbXBSYXQgPSBjb2xuYW1lcyhHRV9mb2xkKVtjKDQsNSw2LDksMTIsMTQsMTcsMjEsMjQsMjUsMjYsMjcsMzAsMzIsMzYsMzcsMzgsMzkpXQoKI0xpc3Rpbmcgb2YgYWxsIGltcG9ydGFudCBjb21wYXJpc29ucz8KSW1wUmF0ID0gYygiU0MwMV9kYXkzL1NDMDFfZGF5MCIsICJTQzAxX2RheTgvU0MwMV9kYXkzIiwgIlNDMDFfZGF5OC9TQzAxX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkzL1NDMDdfZGF5MCIsICJTQzA3X2RheTgvU0MwN19kYXkzIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAKICAgICAgICAgICAiU0MxMF9kYXkzL1NDMTBfZGF5MCIsICJTQzEwX2RheTgvU0MxMF9kYXkzIiwgIlNDMTBfZGF5OC9TQzEwX2RheTAiLCAKICAgICAgICAgICAiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiLAogICAgICAgICAgICJTQzA3X2RheTMvU0MwMV9kYXkzIiwgIlNDMTBfZGF5My9TQzAxX2RheTMiLCAiU0MxMF9kYXkzL1NDMDdfZGF5MyIsCiAgICAgICAgICAgIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IiApCkltcF9mb2xkID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBJbXBSYXQpXQpJbXBfZm9sZDIgPSBJbXBfZm9sZFtyb3dTdW1zKGFicyhJbXBfZm9sZFssNDoyMV0pPj0xLjUpPj0xLF0KCiMgd3JpdGUudGFibGUoSW1wX2ZvbGQsIlNDMSw3LDEwLVRpbWVjb3Vyc2VQTFgtSW1wb3J0YW50RkNfMjAxODA3MjIudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GKQoKSW1wX2ZvbGQgPSByZWFkLmRlbGltKCJTQzEsNywxMC1UaW1lY291cnNlUExYLUltcG9ydGFudEZDXzIwMTgwNzIyLnR4dCIsIHNlcD0iXHQiKQoKI1N1YnNldCB0aGUgTEYgbWVhbiBvZiBpbXBvcnRhbnQgZ2VuZXMgZnJvbSBMb2cyIEZvbGQgQ2hhbmdlIChMRkMpIGNvbXBhcmlzb24gZGF0YSBmcmFtZS4KR0VfSW1wID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lSW1wX2ZvbGQyJGVuc2VtYmxfZ2VuZV9pZCkKCk5lY3JvID0gcmVhZC5kZWxpbSgiS0VHR05lY3JvcHRvc2lzX2hzYTA0MjE3XzA2LTI1LTE4LnR4dCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzID0gRikKTmVjcm8gPSBOZWNyb1tyb3dTdW1zKGlzLm5hKE5lY3JvKSkgPT0gMCwgXQpERV9OZWNybyA9IG1lcmdlKEdFX0ltcCwgTmVjcm8sIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX05lY3JvKSA9IG1ha2UubmFtZXMoREVfTmVjcm9bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChERV9OZWNyb1szOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFKQojIHdyaXRlLnRhYmxlKERFX05lY3JvLCAiS0VHR05lY3JvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCgpBcG9wID0gcmVhZC5kZWxpbSgiS0VHR0Fwb3B0b3Npc19oc2EwNDIxMF8wNi0yNS0xOC50eHQiLCBoZWFkZXI9VCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCkFwb3AgPSBBcG9wW3Jvd1N1bXMoaXMubmEoQXBvcCkpID09IDAsIF0KREVfQXBvcCA9IG1lcmdlKEdFX0ltcCksIEFwb3AsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0Fwb3ApID0gbWFrZS5uYW1lcyhERV9BcG9wWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfQXBvcFszOjI5XSxjbHVzdGVyX2NvbHMgPSBUUlVFLCBzY2FsZSA9ICJyb3ciKQojIHdyaXRlLnRhYmxlKERFX0Fwb3AsICJLRUdHQXBvcHRvc2lzIFNDMSw3LDEwIERFU2VxIExSVC50eHQiLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCkZlcnIgPSByZWFkLmRlbGltKCJLRUdHRmVycm9wdG9zaXNfaHNhMDQyMTZfMDYtMjUtMTgudHh0IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpGZXJyID0gRmVycltyb3dTdW1zKGlzLm5hKEZlcnIpKSA9PSAwLCBdCkRFX0ZlcnIgPSBtZXJnZShHRV9JbXAsIEZlcnIsIGJ5LnggPSAiaGduY19zeW1ib2wiLCBieS55ID0gIkdlbmVOYW1lIikKcm93Lm5hbWVzKERFX0ZlcnIpID0gbWFrZS5uYW1lcyhERV9GZXJyWywiaGduY19zeW1ib2wiXSwgdW5pcXVlID0gVFJVRSkKcGhlYXRtYXAoREVfRmVycls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCiMgd3JpdGUudGFibGUoREVfRmVyciwgIktFR0dGZXJyb3B0b3NpcyBTQzEsNywxMCBERVNlcSBMUlQudHh0Iiwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpCgoKYGBgCgojIyMjIDQuIERpZmZlcmVudCBMQyBjb21wYXJpc29ucy4gQmV0d2VlbiBzdWJjbG9uZXMgYW5kIGF0IGJhc2VsaW5lIHZzIGlkbGluZy4KWnNjb3JlIGhlYXRtYXBzIG9mIHJlbGV2YW50IGNvbXBhcmlzb25zIGNhbiBiZSBtYWRlIGFzIGluIGFib3ZlIHRvIHZpc3VhbGl6ZS4KCmBgYHtyIGV2YWw9RkFMU0V9CgojVVNFUyBBQk9WRSBDT0RFIFRPIExJTkUgMjgwLiBSdW4gdGhhdCBwc2V1ZG8tZnVuY3Rpb24uCgojIGxpYnJhcnkocGhlYXRtYXApCiNDb21wYXJpc29ucyBvZiBkaWZFeCBiZXR3ZWVuIHN1YmNsb25lcyBhdCBiYXNlbGluZSBhbmQgaWRsaW5nCkJldHdlZW5CYXNlID0gYygiU0MwN19kYXkwL1NDMDFfZGF5MCIsICJTQzEwX2RheTAvU0MwMV9kYXkwIiwgIlNDMTBfZGF5MC9TQzA3X2RheTAiKQpCZXR3ZWVuSWRsZSA9IGMoIlNDMDdfZGF5OC9TQzAxX2RheTgiLCAiU0MxMF9kYXk4L1NDMDFfZGF5OCIsICJTQzEwX2RheTgvU0MwN19kYXk4IikKIAoKI1Vuc3VyZSBvZiBob3cgc3RyaWN0IHRvIG1ha2UgdGhlIGN1dG9mZi4gU2hvdWxkIGFsbCB0aGUgZ2VuZXMgYmV0d2VlbiBjbG9uZXMgYmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkICgzKSBvciBpcyBhIHNpbmdsZSBkaWZmZXJlbmNlIHN1ZmZpY2llbnQ/CkJ0d19iID0gR0VfZm9sZFssYygiZW5zZW1ibF9nZW5lX2lkIiwgImhnbmNfc3ltYm9sIiwgInBhZGoiLCBCZXR3ZWVuQmFzZSldCkJ0d19iMSA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MSxdCkJ0d19iMiA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MixdCkJ0d19iMyA9IEJ0d19iW3Jvd1N1bXMoYWJzKEJ0d19iWyw0OjZdKT49MS41KT49MyxdCgpCdHdfaSA9IEdFX2ZvbGRbLGMoImVuc2VtYmxfZ2VuZV9pZCIsICJoZ25jX3N5bWJvbCIsICJwYWRqIiwgQmV0d2VlbklkbGUpXQpCdHdfaTEgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTEsXQpCdHdfaTIgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTIsXQpCdHdfaTMgPSBCdHdfaVtyb3dTdW1zKGFicyhCdHdfaVssNDo2XSk+PTEuNSk+PTMsXQoKI1RoaXMgZG9lcyBub3QgYWNjb3VudCBmb3Igc2FtZSBkaXJlY3Rpb24gb2YgY2hhbmdlLiBUaGlzIGNhbiBiZSBwbG90dGVkIGluIGEgaGVhdG1hcCB0byB2aWV3LgojTWVtYmVycyB0aGF0IHdlcmUgImxvc3QiIGJ5IHRoZSBiYXNlbGluZSBjb25kaXRpb24gYXQgYmVpbmcgZGlmZmVyZW50LiBXZXJlIG5vIGxvbmdlciBmb3VuZCBkaWZmRXggYmV0d2VlbiB0aGUgc3ViY2xvbmVzIHdoZW4gY29tcGFyaW5nIGJhc2VsaW5lIHRvIGlkbGluZyBERUdzLgpMb3N0REVHX2JfMSA9IHN1YnNldChCdHdfYjEsIUJ0d19iMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kxJGVuc2VtYmxfZ2VuZV9pZCkKTG9zdERFR19iXzIgPSBzdWJzZXQoQnR3X2IyLCFCdHdfYjIkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMiRlbnNlbWJsX2dlbmVfaWQpCkxvc3RERUdfYl8zID0gc3Vic2V0KEJ0d19iMywgIUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjTWFrZSBoZWF0bWFwCkxvc3RERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMyRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfM19tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8zX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKI01lbWJlcnMgdGhhdCByZW1haW5lZCBkaWZmZXJlbnQuIApTdGF0aWNERUdfYl8xID0gc3Vic2V0KEJ0d19iMSxCdHdfYjEkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQpClN0YXRpY0RFR19iXzIgPSBzdWJzZXQoQnR3X2IyLEJ0d19iMiRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCkKU3RhdGljREVHX2JfMyA9IHN1YnNldChCdHdfYjMsIEJ0d19iMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2kzJGVuc2VtYmxfZ2VuZV9pZCkKCiMjU29tZSBIR05DX3N5bWJvbHMgYXJlIGR1cGxpY2F0ZXMhIEkgc3dpdGNoZWQgdG8gZW5zZW1ibF9nZW5lX2lkIHRvIGZpeC4KU3RhdGljREVHX2lfMyA9IHN1YnNldChCdHdfaTMsIEJ0d19pMyRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IzJGVuc2VtYmxfZ2VuZV9pZCkKCgojI01ha2UgaGVhdG1hcApTdGF0aWNERUdfYl8zX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVTdGF0aWNERUdfYl8zJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhbikgPSBtYWtlLm5hbWVzKFN0YXRpY0RFR19iXzNfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKFN0YXRpY0RFR19iXzNfbWVhbls0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgoKI01lbWJlcnMgdGhhdCAiZ2FpbmVkIiBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSBzdWJjbG9uZXMgaW4gaWRsaW5nLiAgCkdhaW5ERUdfaV8xID0gc3Vic2V0KEJ0d19pMSwgIUJ0d19pMSRlbnNlbWJsX2dlbmVfaWQlaW4lQnR3X2IxJGVuc2VtYmxfZ2VuZV9pZCkKR2FpbkRFR19pXzIgPSBzdWJzZXQoQnR3X2kyLCAhQnR3X2kyJGVuc2VtYmxfZ2VuZV9pZCVpbiVCdHdfYjIkZW5zZW1ibF9nZW5lX2lkKQpHYWluREVHX2lfMyA9IHN1YnNldChCdHdfaTMsICFCdHdfaTMkZW5zZW1ibF9nZW5lX2lkJWluJUJ0d19iMyRlbnNlbWJsX2dlbmVfaWQpCgojI01ha2UgaGVhdG1hcApHYWluREVHX2lfM19tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lR2FpbkRFR19pXzMkZW5zZW1ibF9nZW5lX2lkKQpyb3cubmFtZXMoR2FpbkRFR19pXzNfbWVhbikgPSBtYWtlLm5hbWVzKEdhaW5ERUdfaV8zX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQpwaGVhdG1hcChHYWluREVHX2lfM19tZWFuWzQ6MTJdLGNsdXN0ZXJfY29scz1GQUxTRSwgc2NhbGUgPSAicm93IikKCgojTWVtYmVycyB0aGF0IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJldHdlZW4gaWRsaW5nICg4ZGF5KSBhbmQgYmFzZWxpbmUgd2l0aGluIHN1YmNsb25lcy4gVGhvc2Ugd2l0aCBzaGFyZWQgZGlmZkV4IG1heSBiZSBjb252ZXJnZW50IGFjcm9zcyBtdWx0aXBsZSBzdWJjbG9uZXMgZGVwZW5kaW5nIG9uIGRpcmVjdGlvbiBvZiBleHByZXNpc29uIGNoYW5nZS4KRW5kcG9pbnQgPSBjKCJTQzAxX2RheTgvU0MwMV9kYXkwIiwgIlNDMDdfZGF5OC9TQzA3X2RheTAiLCAiU0MxMF9kYXk4L1NDMTBfZGF5MCIpCkJ0b0lkbGUgPSBHRV9mb2xkWyxjKCJlbnNlbWJsX2dlbmVfaWQiLCAiaGduY19zeW1ib2wiLCAicGFkaiIsIEVuZHBvaW50KV0KQnRvSWRsZV8xID0gQnRvSWRsZVtyb3dTdW1zKGFicyhCdG9JZGxlWyw0OjZdKT49MS41KT49MSxdCkJ0b0lkbGVfMiA9IEJ0b0lkbGVbcm93U3VtcyhhYnMoQnRvSWRsZVssNDo2XSk+PTEuNSk+PTIsXQpCdG9JZGxlXzMgPSBCdG9JZGxlW3Jvd1N1bXMoYWJzKEJ0b0lkbGVbLDQ6Nl0pPj0xLjUpPj0zLF0KCiMjTWFrZSBoZWF0bWFwCkJ0b0lkbGVfMl9tZWFuID0gc3Vic2V0KEdFX21lYW4sR0VfbWVhbiRlbnNlbWJsX2dlbmVfaWQlaW4lQnRvSWRsZV8yJGVuc2VtYmxfZ2VuZV9pZCkKcm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuKSA9IG1ha2UubmFtZXMoQnRvSWRsZV8yX21lYW5bLCJoZ25jX3N5bWJvbCJdLCB1bmlxdWUgPSBUUlVFKQoKQnRvSWRsZV8yX21lYW5faW5jRXhwID0gQnRvSWRsZV8yX21lYW5bd2hpY2goQnRvSWRsZV8yX21lYW4kU0MwMV9kYXkwIDwgQnRvSWRsZV8yX21lYW4kU0MwMV9kYXk4KSxdCkJ0b0lkbGVfMl9tZWFuX2luY0V4cCA9IEJ0b0lkbGVfMl9tZWFuX2luY0V4cFt3aGljaChCdG9JZGxlXzJfbWVhbl9pbmNFeHAkU0MwN19kYXkwIDwgQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMDdfZGF5OCksXQpCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2goQnRvSWRsZV8yX21lYW5faW5jRXhwJFNDMTBfZGF5MCA8IEJ0b0lkbGVfMl9tZWFuX2luY0V4cCRTQzEwX2RheTgpLF0KCkxvc3RERUdfYl8yX21lYW4gPSBzdWJzZXQoR0VfbWVhbixHRV9tZWFuJGVuc2VtYmxfZ2VuZV9pZCVpbiVMb3N0REVHX2JfMiRlbnNlbWJsX2dlbmVfaWQpCnJvdy5uYW1lcyhMb3N0REVHX2JfMl9tZWFuKSA9IG1ha2UubmFtZXMoTG9zdERFR19iXzJfbWVhblssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCnBoZWF0bWFwKExvc3RERUdfYl8yX21lYW5bNDoxMl0sY2x1c3Rlcl9jb2xzPUZBTFNFLCBzY2FsZSA9ICJyb3ciKQoKQnRvSWRsZUluY0V4cF9ERWJldHdlZW5TQ3MgPSBCdG9JZGxlXzJfbWVhbl9pbmNFeHBbd2hpY2gocm93Lm5hbWVzKEJ0b0lkbGVfMl9tZWFuX2luY0V4cCkgJWluJSByb3cubmFtZXMoTG9zdERFR19iXzJfbWVhbikpLF0KCnBoZWF0bWFwKEJ0b0lkbGVfMl9tZWFuX2luY0V4cFs0OjEyXSxjbHVzdGVyX2NvbHM9RkFMU0UsIHNjYWxlID0gInJvdyIpCgojIGxpYnJhcnkoZGV2dG9vbHMpCiMgIyBpbnN0YWxsX2dpdGh1Yigid2phd2FpZC9lbnJpY2hSIikKIyBsaWJyYXJ5KGVucmljaFIpCmRicyA8LSBsaXN0RW5yaWNockRicygpCmhlYWQoZGJzKQpkYnMgPC0gYygiR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiLCAiR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiLCAiR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiKQoKZW5yaWNoZWQgPC0gZW5yaWNocihyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSwgZGJzKQoKVmlldyhlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dKQpWaWV3KGVucmljaGVkW1siR09fQ2VsbHVsYXJfQ29tcG9uZW50XzIwMTUiXV0pClZpZXcoZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSkKCmVucmljaGVkX01GX3NpZyA8LSBlbnJpY2hlZFtbIkdPX01vbGVjdWxhcl9GdW5jdGlvbl8yMDE1Il1dW2VucmljaGVkW1siR09fTW9sZWN1bGFyX0Z1bmN0aW9uXzIwMTUiXV0kQWRqdXN0ZWQuUC52YWx1ZTwwLjA1LF0KZW5yaWNoZWRfTUZfc2lnX2RmIDwtIGRhdGEuZnJhbWUoZW5yaWNoZWRfTUZfc2lnWyxjKDEsNCw5KV0pCndyaXRlLmNzdihlbnJpY2hlZF9NRl9zaWdfZGYsICJlbnJpY2hlZF9NRl9zaWduaWZpY2FudC5jc3YiKQoKZW5yaWNoZWRfQlBfc2lnIDwtIGVucmljaGVkW1siR09fQmlvbG9naWNhbF9Qcm9jZXNzXzIwMTUiXV1bZW5yaWNoZWRbWyJHT19CaW9sb2dpY2FsX1Byb2Nlc3NfMjAxNSJdXSRBZGp1c3RlZC5QLnZhbHVlPDAuMDUsXQplbnJpY2hlZF9CUF9zaWdfZGYgPC0gZGF0YS5mcmFtZShlbnJpY2hlZF9CUF9zaWdbLGMoMSw0LDkpXSkKIyB3cml0ZS5jc3YoZW5yaWNoZWRfQlBfc2lnX2RmLCAiZW5yaWNoZWRfQlBfc2lnbmlmaWNhbnQuY3N2IikKCkdpbmlfc2NHZW5lcyA8LSBjKCJBUE9FIiwgIkJDQU4iLCAiQ0VTMSIsICJDSVRFRDEiLAogICAgICAgICAgICAgICAgICAiQ1BNIiwgIkNUU0YiLCAiRENUIiwgIkVETlJCIiwgCiAgICAgICAgICAgICAgICAgICJFR1IxIiwgIkVTUlAxIiwgIkZTVEwxIiwgIk1BTEFUMSIsCiAgICAgICAgICAgICAgICAgICJNQVAySzYiLCAiTUNGMkwiLCAiTUxBTkEiLCAiTVhENCIsCiAgICAgICAgICAgICAgICAgICJPQ0EyIiwgIlBNRUwiLCAiU0VNQTZBIiwgIlNOQUkyIiwKICAgICAgICAgICAgICAgICAgIlNPWDQiLCAiVFNQQU4xMCIpCmVucmljaGVkX3NjIDwtIGVucmljaHIoR2luaV9zY0dlbmVzLCBkYnMpCgpyb3cubmFtZXMoQnRvSWRsZV8yX21lYW5faW5jRXhwKSAlaW4lIEdpbmlfc2NHZW5lcwoKYGBgCgoKCiMjIyMgUmVzdCBvZiBKYWNrJ3MgQW5hbHlzaXMgIyMjIwpgYGB7ciBldmFsPUZBTFNFfQojVmlzdWFsbHkgaW5zcGVjdCB0cmVuZGluZyBtZW1iZXJzIGZyb20gaGVhdG1hcHMuCiNQbG90cyBvZiBzcGVjaWZpYyB0cmVuZGluZyBtZW1iZXJzPwpwIDwtIGdncGxvdChkYXRhPWRmMiwgYWVzKHg9ZG9zZSwgeT1sZW4sIGZpbGw9c3VwcCkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yPSJibGFjayIsIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKCkpKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIyBOT1RFOiBjb2RlIGJlbG93IHJldXNlcyBvYmplY3QgbmFtZXMuLi4gV0lMTCBPVkVSV1JJVEUhCmBgYHtyIGV2YWw9RkFMU0V9CiNHTE0gQ29lZiBIZWF0bWFwLgpiZXRhcyA8LSBjb2VmKGRkcykKdG9wR2VuZXMgPC0gd2hpY2gocmVzJHBhZGogPD0gMC4wMDEpCm1hdCA8LSBkYXRhLmZyYW1lKGJldGFzW3RvcEdlbmVzLF0pCm1hdCRlbnNlbWJsX2dlbmVfaWQgPSByb3cubmFtZXMobWF0KQptYXQkcGFkaiA9IHJlc1t0b3BHZW5lcywicGFkaiJdCiMgZW5zZW1ibCA8LSB1c2VNYXJ0KCJlbnNlbWJsIikKIyBtYXJ0IDwtIHVzZURhdGFzZXQoImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIsIG1hcnQgPSBlbnNlbWJsKQojIGdlbmVzID0gcm93Lm5hbWVzKG1hdCkKIyBHX2xpc3QgPC0gZ2V0Qk0oYXR0cmlidXRlcz0gYygiZW5zZW1ibF9nZW5lX2lkIiwiaGduY19zeW1ib2wiKSwKIyAgICAgICAgICAgICAgICAgZmlsdGVycz0gImVuc2VtYmxfZ2VuZV9pZCIsCiMgICAgICAgICAgICAgICAgIHZhbHVlcz1nZW5lcywKIyAgICAgICAgICAgICAgICAgbWFydD1tYXJ0KQoKIyBHRV9kYXRhIDwtIG1lcmdlKG1hdCwgR19saXN0LCBieSA9ICJlbnNlbWJsX2dlbmVfaWQiKQojIHJvd25hbWVzKEdFX2RhdGEpIDwtIG1ha2UubmFtZXMoR0VfZGF0YVssImhnbmNfc3ltYm9sIl0sIHVuaXF1ZSA9IFRSVUUpCiMgR0VfZGF0YSA9IEdFX2RhdGFbb3JkZXIoR0VfZGF0YSRwYWRqKSxdCgoKI1NvcnRpbmcgc2NyaXB0IHRvIHBpY2sgb3V0IGVudHJpZXMgZ3JlYXRlciB0aGFuIG9yIGxlc3MgdGhhbiArLTEKZWcgPSBjKCkKZm9yKGkgaW4gMzoxMCl7CiAgZyA9IHdoaWNoKEdFX2RhdGFbLGldID4gMyB8IEdFX2RhdGFbLGldIDwgLTMpCiAgZWcgPSBjKGVnLGcpCn0KZWcgPSB1bmlxdWUoZWcpCgptYXQgPSBHRV9kYXRhW2VnLC1jKDE6MiwxMSwxMildCnRociA8LSAzIAptYXRbbWF0IDwgLXRocl0gPC0gLXRocgptYXRbbWF0ID4gdGhyXSA8LSB0aHIKIyBsaWJyYXJ5KHBoZWF0bWFwKQpwaGVhdG1hcChtYXQsIGNsdXN0ZXJfY29scyA9IEZBTFNFKQoKIyBzc2RnID0gc2RnWzE6MTAwMCwgXQpkaW0oc2RnKQpoZWFkKHNkZykKCmBgYAoKCg==